標籤:
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 discardable13 14 } else {15 16 //data has been discarded, so recreate data and complete operation17 18 data = ...19 20 [data endContentAccess]; //done with data21 22 }23 24 //data is able to be discarded at this point if memory is tightView Code
3、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可以提供優雅的自動刪減功能,而且還是安全執行緒的,此外它與字典不同,它並不會拷貝鍵。直接複製鍵引用。
Caching和Purgeable Memory (譯)