Caching和Purgeable Memory (译)
Caching和Purgeable Memory對于開發者來說是一個至關重要的資源,尤其是當我們需要處理那些需要超大內存以及計算時間的對象或者是當計算機向磁盤寫入數據時導致應用程序陷入停滯時特別有用處。
一、Caching概述
Caching是一個可以顯著提高應用程序性能的對象或者是數據的集合。
1、為什么使用 Caching
開發者可以使用caches來存儲那些需要頻繁訪問的對象并且這些對象中包含了需要大量CPU時間計算得來數據。由于這些值不需要再重復計算,重用這些數據對象有助于提供我們的應用程序性能。然而,對于應用程序來說,這些對象并不是至關重要的,并且可以在內存吃緊的時候釋放掉。如果這些對象被釋放,那么它包含的值就會在需要的時候重新計算。
2、 Caching可能引發的問題
盡管從性能的角度上來講,caching可以帶來巨大的好處,但是它同樣也有一些缺點。最重要的是,緩存會使用大量的內存空間。當我們緩存了大量的大數據對象時,可能會導致沒有足夠的內存供其它應用程序使用,這個時候計算機可能會啟動頁面置換(OSX)或者是通知其它應用程序釋放內存(IOS)以此來獲取內存空間。
3、解決方案
Cocoa 框架提供了一個NSCache對象來管理我們任何需要cache的對象,它是一個對象容器。NSCache對象類和NSDictionary對象類非常相似,它們都擁有鍵值對。不同的是,NSCache對象是一個"活性的Cache",也就是說,當我們的內存足夠用時,他會緩存足夠多的對象。當內存不夠時,它會自動清理其中的某些對象來釋放內存空間供其它應用程序使用。然后,如果我們需要再次使用這些銷毀的對象,這些對象的值會重新計算。
NSCache提供了兩個有用的限制特性:限制緩存項的數目和限制緩存項總的內存消耗。通過調用?setCountLimit:可以限制緩存項的數目。例如,如果我們嘗試將第11個對象緩存到一個只有10個緩存項數量的NSCache中,NSCache可能會自動銷毀其中的一個對象。
當我們向緩存中添加緩存項時,我們可以通過?setTotalCostLimit:指定一個cost值給這個緩存,來設置最大可緩存的所有對象的內存大小是多少。當緩存超過這個限制時,他可能會自動銷毀其中的某些對象讓這個緩存值低于最大的內存大小限制。這個自動銷毀處理機制并不能保證性能,嘗試操作這個值有時候會產生性能上的開銷,可能獲取某一個資源的大小的開銷還不足以抵消使用緩存帶來的好處。當然,如果你緩存的數據沒有什么大的用處,傳入0就行了,當然你也可以通過?setObject:forKey:這種KVC的方式來設置這個值。傳0并不會帶來任何開銷。
緩存項數目和緩存總量并不是絕對的指標,也就是說可用系統資源趨于緊張時,可能會刪減某個對象不代表一定會刪減某個對象。刪減對象所遵照的順序有具體的實現而定。這尤其說明:想通過調整開銷值來迫使緩存優先刪減某些對象并不是一個好主意。
二、使用Purgeable Memory
Cocoa框架提供了NSPurgeableData對象類來幫助應用程序不會使用過大的內存空間。NSPurgeableData遵守NSDiscardableContent協議,任何實現了該協議的對象類都可以在其它對象使用完畢該對象實例后允許內存被釋放。當我們創建了那些擁有很多可隨意處理的子控件的對象的時候,我們應該實現這個協議。另外,NSPurgeableData對象沒有必要一定要與NSCache對象類配合使用。我們只需要使用它一個就能獲取到Purgeable特性。
1、使用Purgeable Memory的優勢
通過使用purgeable memory,我們可以在系統需要時快速的恢復內存,因此可以提高性能。因為頁面置換是一個十分耗時的操作,所以當那些被標記為purgeable的內存被虛擬內存系統(VMS)回收時不會被頁換出到磁盤。取而代之的是,這些對象占用的內存數據會被銷毀,如果之后需要用到時會再次計算即可。(Purgeable Memory占用的內存不會因為內存不足置換到backing store,僅僅只會銷毀,)
需要注意的時,如果我們使用purgeable memory,它所占用的那部分內存在使用之前會被鎖定。這個鎖定機制是十分有必要的,它確保了不會因為自動回收機制釋放了那些你想要在之后訪問的內存數據。同樣,這個鎖定機制頁確保了虛擬內存系統沒有銷毀這部分數據。NSPurgeableData對象類通過這樣一個簡單的鎖定機制來確保數據在讀取之時時安全的。
2、如何實現Purgeable Memory
NSPurgeableData對象類的使用非常簡單,這個對象類僅僅實現了?NSDiscardableContent協議。對于?NSDiscardableContent對象的生命周期來說,核心思想就是引用計數。當一個對象使用一塊內存時,這塊內存區域的引用計數>=1。當它不在被使用并且可以被銷毀時,它的引用計數=0。
當引用計數變為0時,在內存吃緊的情況下,這塊內存就會被回收。為了釋放這塊內存區域,通過調用對象的l?discardContentIfPossible方法,我們可以釋放這塊引用計數為0的內存區域。
默認情況下,當一個NSPurgeableData對象初始化的時候,它的引用計數變量值為1,并且可以安全的被訪問。為了訪問這個purgeable memory,僅僅調用?beginContentAccess方法即可。這個方法首先會檢查這個對象的數據是否被銷毀。如果這個數據仍然在,它將會增加這個對象指向的內存的引用計數,并且返回YES。如果這個對象的數據已經被銷毀了,這個方法就會返回NO。當我們完成對這個對象的訪問后,調用endContentAccess方法即可,這個方法會減少這塊內存區域的引用計數,并允許內存在需要是釋放它。只有當?beginContentAccess方法返回YES時,我們才可以去訪問這個對象所指向的內存空間。
當系統可用內存減少時,系統或客戶端對象通過調用discardContentIfPossible方法來銷毀purgeable數據,當對象所指向的內存引用計數為0時,這個方法僅僅會銷毀上面的數據,然后不再做其它任何操作。如果這個內存被銷毀了,那么?isContentDiscarded方法會返回YES。下面是一個使用NSPurgeableData對象的例子:
1 NSPurgeableData * data = [[NSPurgeableData alloc] init]; 2 3 [data endContentAccess]; //Don't necessarily need data right now, so mark as discardable. 4 5 //Maybe put data into a cache if you anticipate you will need it later. 6 ... 7 8 if([data beginContentAccess]) { //YES if data has not been discarded and counter variable has been incremented 9 10 ...Some operation on the data... 11 12 [data endContentAccess] //done with the data, so mark as discardable 13 14 } else { 15 16 //data has been discarded, so recreate data and complete operation 17 18 data = ... 19 20 [data endContentAccess]; //done with data 21 22 } 23 24 //data is able to be discarded at this point if memory is tight View Code3、Purgeable Memory和NSCache
當一個實現了?NSDiscardableContent協議的對象放在NSCache對象中去時,cache對象就維持了一個對該對象的強引用。然后,如果這個對象的內容已經被銷毀(discard)了,這個緩存的evictsObjectsWithDiscardedContent的值將會被設置為YES,然后這個對象會自動從緩存中移除。
4、一些使用Purgeable Memory的警告
值得注意的是Purgeable memory是那些大容量對象才可以直接使用的內存。Purgeable API也是以多頁大小虛擬內存對象來設計,這就使得我們很難把一個很小的緩存元素標記為purgeable。這個 cache API會做一些必須的記錄以支持將讓的緩存元素也可以使用purgeable memory。同樣的,有時候通過API為這些緩存元素分配內存空間是不合適的,當我們可以十分方便的創建一個對象,或這個對象在不同層分配,并且這個層已經對它做了緩存。這種情況,不適合使用purgeable memory。
5、什么時候使用Purgeable Memory
當擦除這個對象的性能開銷小于頁面置換的性能開銷時,我們可以考慮采用Purgeable Memory------頁面置換的性能開銷遠大于再次使用這個對象時重新計算數據值的性能開銷。很多時候緩存沒沒有遵循上面的規律,它們的一些緩存項很有可能以后都不會再次使用。同樣的,那些能夠輕松計算出數據的緩存項可以考慮使用purgeable memory,因為重新計算這些數據并不會耗費應用程序多少性能。
?附:
總結:
1、使用Purgeable Memory可以讓我們可以安全的使用某一個對象內存,可以通過方法判斷該對象內存上的數據是否已經被擦除。
? ? ? 2、Purgeable Memory對應的內存不會被頁換出到backing store,在內存不足時會被直接擦除,釋放出的內存可供其它地方使用。
3.purgeable memory 定義可直接擦除,而不需要進行頁面置換。ios中所有對象都是purgeable ,但是并不是所有對象都可以知曉自己使用的內存是否被擦除,這就必須借助實現了nsdiscardablecontent協議的對象來完成更精確的控制。
猜想:內核系統中維持了一個Purgeable 內存區域,里面維護了一些Purgeable Page列表,這種類型的Page也會與物理內存中的頁有一個映射關系,當我們釋放其中某個對象對應的內存頁時,實際的物理內存區域會被系統回收供本應用程序或其它應用程序使用,但是Purgeable內存區域中也會維護一個列表來指示,這個對象的內存頁當前已經被擦除。
?
實際用處:
1、可以使用 NSPurgeableData與NSData搭配使用,如果某個對象所占的內存能夠根據需要隨時丟棄,那么就可以實現該協議所定義的方法。也就是說當系統資源緊張時,可以把保存NSPurgeableData對象的那塊內存釋放掉。NSDiscardableContent協議里定義了名為isContentDiscarded方法,可以用來查詢相關內存是否已經釋放。
2、如果需要訪問某個NSPurgeablData對象,可以調用其beginContentAccess方法,告訴系統現在還不應該丟棄自己所占用的內存。用完之后,調用endContentAccess方法,告訴系統在必要時可以丟棄自己所占據的內存。這些調用可以嵌套,所以說,它們就像遞增與遞減引用計數所用的方法一樣。只有對象的引用計數為0時才可以丟棄。
3、如果將NSPurgeableData對象加入NSCache,在該對象被系統丟棄時也會自動從緩存中移除。通過NSCache的evictsObjectsWithDiscardedContent屬性,可以開啟或關閉此功能。(IOS系統中在發生內存不足會強制應用程序清理內存)。
4、實現緩存時應該選用NSCache而非NSDictionary對象。因為NSCache可以提供優雅的自動刪減功能,而且還是線程安全的,此外它與字典不同,它并不會拷貝鍵。直接復制鍵引用。
? ?
?
轉載于:https://www.cnblogs.com/forwk/p/4813065.html
總結
以上是生活随笔為你收集整理的Caching和Purgeable Memory (译)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 搜索学习(ing...)
- 下一篇: Java多线程与并发库高级应用 学习笔记