全部應用程序都要管理內存。應用程序的內存管理包括用於肯定什麼時候分配內存,分配多少內存,什麼時候將內容放入回收站,以及什麼時候清空回收站的準則。MMgc是 Flash Player用於幾乎全部內存分配工做的通用內存管理器。理解MMgc如何管理內存是優化您的代碼和您應用程序的性能的一個重要部分。
垃圾收集器自動回收的內存被視爲「受管理的內存」。垃圾收集器肯定內存什麼時候再也不被應用程序使用並回收它。本文分析Flash Player 11和AIR 3中的內存分配、垃圾收集流程和新的pauseForGCIfCollectionImminent()API。
內存分配
Flash Player使用一個頁面分配程序(GCheap)來從OD分配大塊(幾MB)的內存。Gcheap而後將大內存塊分解爲較小的4K頁面,並根據須要將這些頁面提供給垃圾收集(GC)內存管理器。
圖1. GCHeap從OS分配內存,將它分解爲4K的頁面,並將這些頁面提供給GC。
GC而後使用這些4K頁面爲系統中不大於2K的對象提供內存。
圖2. 4K頁面由GC分配給小於2K的對象
對於大於2K的對象(位圖、視頻、文件等),GCHeap向一個大型的內存分配程序提供一組連續的4K內存塊。
當一個大內存塊中幾乎全部4K頁面都分配了時,Flash Player運行垃圾收集來回收未使用的內存,而後GCHeap嘗試從OS分配更多內存。換句話說,垃圾收集僅由內存分配觸發。這一事實很重要,在測試和 分析期間必定要記住,由於它意味着空閒應用程序的內存使用從不會改變。
堆和堆棧
堆是分配給在運行時建立或初始化的任何對象的內存。堆上的對象會一直存在到它們被垃圾收集。
圖3. 對象A存在於堆上。它由堆棧上的局部變量o引用。(圖字:堆棧內存 堆內存)
堆棧是存儲在編譯時定義的全部變量的內存。堆棧內存以一種順序方式使用和重用。推送操做將一些內容添加到堆棧頂部。彈出操做從堆棧頂部刪除一些內容。訪問堆棧中間的內容的惟一方式是刪除它上方的全部內容。
局部方法變量、參數和關於在一個方法完成時返回到何處的信息,在方法運行時被推送到堆棧上。對堆棧的更改發生得很是快。對象的堆棧引用可能很是短暫。這些對象引用可能存在於堆棧上,但分配給這些對象的內存來自堆。
圖4. 局部變量在定義時被推送到堆棧上。關於在一個方法完成時返回何處的信息也推送到堆棧上。
Flash運行時垃圾收集實現
Flash Player和AIR結合使用延遲的引用計數和保守的標記並清除(mark-and-sweep)方法。
延遲的引用計數
在延遲的引用計數中,堆和堆棧引用之間存在區別。由於堆棧變化很快,並可能包含很是短暫的引用,因此引用計數不會在堆棧引用上執行。而在堆上爲引用維護引用計數。
圖5. 對象會跟蹤它們擁有多少個引用。
堆上的每一個對象會跟蹤指向它的信息數量。每次您建立一個對象的引用,該對象的引用計數就會遞增。當您刪除一個引用時,該對象的引用計數會遞減。若是對象的 引用計數爲0(沒有任何信息指向它),它會被添加到零計數表(Zero Count Table,ZCT)中。當ZCT填滿後,就會掃描堆棧以查找任何從堆棧到ZCT上的對象的引用。ZCT上任何沒有堆棧引用的對象都會被刪除。
延遲引用計數的一個問題是循環引用。若是ObjectA和ObjectB彼此引用,而系統中沒有其餘對象指向它們,它們將從不會擁有一個零引用計數,所以從不知足使用引用計數進行垃圾收集的資格。這時可使用「標記並清除」的垃圾收集方法。
圖6. Object A和Object B彼此引用,但沒有其餘引用。
標記/清除
在Flash Player或AIR中運行的應用程序具備多個GCRoot。您能夠將GCRoot視爲一個樹的一部分,它將應用程序的對象看成樹枝。舞臺是一個 GCRoot。加載程序是GCRoot。某些菜單是GCRoot。讓在供應用程序使用的每一個對象可從應用程序內的一個GCRoot訪問。GCRoot從不 會被垃圾收集。
應用程序中的每一個對象有一個「標記位」。當垃圾收集的標記階段開始時,全部這些標記位會被清除。MMgc會跟蹤應用程序中的全部GCRoot。垃圾收集器 首先從這些根開始,跟蹤每一個對象併爲它到達的每一個對象設置標記位。任何再也不可以從任何根到達的對象也再也不可以從應用程序的任何地方到達——它的標記位不會 在標記階段設置。收集器完成對它找到的全部對象進行標記以後,就會開始清除階段。任何沒有設置標記位的對象都會被銷燬,它的內存會被回收。
圖7. 一個循環引用中的對象沒有被標記。
圖7顯示,每一個可從Gcroot到達的對象都設置了本身的標記位(藍色)。一個循環引用中的兩個對象(ObjectA和ObjectB)不可從GCRoot到達。它們的標記位將不會設置。所以,即便它們沒有零引用計數,這兩個對象也會被垃圾收集。
弱引用
Flash Player也能夠維持對某些類型的對象的「弱引用」。弱引用是一種對垃圾收集器的正常跟蹤過程(跟隨全部根來查找可到達的對象的過程)不可見的引用。
當您實例化一個新字典時,能夠代表您但願它與字典的鍵創建較弱的關聯。
var dictionary = new Dictionary( true );
d[ someObject ] = someValue;
您也能夠在添加事件監聽器時,將addEventListener()的函數useWeakReference參數設置爲true。
obj.addEventListener( "type", handler, false, 0, true );
在這兩種狀況下,您都會要求Flash Player在兩個對象之間創建引用,但以一種較弱的方式保持該引用。具體來說,這意味着這個具體的引用在標記期間不會被跟隨。
圖8. 若引用在標記期間不會被跟蹤。
在這種狀況下,到Object B的惟一路徑是弱的。在跟蹤期間將不會通過它,所以Object B不會被標記,並會被收集。可是,若是還有另外一個到Object B的強路徑,Object B將被標記並被持久化。
圖9. 具備強引用的對象將在跟蹤期間被找到並標記。
您應該始終清理未使用的引用,從字典刪除未使用的項,以及使用removeEventListener()。可是,有時清理未使用的引用不切實際或沒法作 到。好比在您的類在您不知情的狀況下實例化和銷燬時——項渲染器就是經過這種方式使用的。在這些狀況下,維持對象的若引用將容許Flash Player最終刪除它們並回收內存。
保守收集
MMgc被視爲一種保守的標記/清除收集器。MMgc沒法肯定內存中的某些值是對象指針(內存地址)仍是數字值。爲了不意外地收集值可能指向的對 象,MMgc假設每一個值均可以是一個指針。所以,一些沒有實際被指向的對象將從不被收集,將被視爲一種內存泄漏。儘管您但願最小化內存泄漏以優化性能,但 由保守的GC所致使的偶然泄漏多是隨機的,不會隨時間增加,而且對應用程序性能的影響比開發人員致使的泄漏小得多。
增量收集
不幸的是,垃圾收集可致使Flash Player在收集過程完成時按期暫停。這種暫停與應用程序當前運行的內存量成正比。它可能比但願的時間更長,在一些程序中能夠察覺到。
標記階段是垃圾收集過程當中最消耗時間的部分。因爲此事實,標記過程使用一個動做隊列和一個3色算法增量化了。該隊列在標記增量之間維護標記狀態。
表1. 3色算法php
|
黑色對象已標記,再也不位於隊列中。 |
|
灰色對象位於隊列中,還未被標記。 |
|
白色對象既未標記也不在隊列中。 |
在標記階段的開始,因此GCRoot被推送到隊列中並變爲灰色。
圖10. GCRoot在推送到工做隊列中時變成灰色。
隨着標記過程的繼續,標記的對象變爲灰色,並從工做隊列刪除。
圖11. 標記的對象是黑色的,再也不在工做隊列中。
此過程會正常繼續進行,直到將一個新對象(白色)添加到一個黑色對象上。當發生此狀況時,白色對象從不會設置它的標記位,由於它們的GCRoot已標記。不設置它們的標記位,它們將在清除階段被垃圾收集。
圖12. 新對象被添加到之前標記的對象上。
要預防此問題,能夠在MMgc中使用一個白色邊界來強制將任何添加到黑色對象上的白色對象當即添加到工做隊列中。
圖13. 添加到之前標記的對象中的新對象被當即添加到工做隊列中。
經過使用工做隊列和3色算法,可開始和中止標記階段來幫助避免長時間、意外的垃圾收集暫停。
迫近度
標記階段多是垃圾收集中最耗時的部分,但實際上清空回收站(從新分配空閒內存)也比較耗時。從新分配還可能致使應用程序暫停。垃圾收集器離標記階段的完成和清除(從新分配)階段的開始的時間稱爲「迫近度(imminence)」
圖14. 迫近度(圖字:標記 暫停、迫近度增加)
public static function pauseForGCIfCollectionImminent(imminence:Number = 0.75):void
是Flash Player 11和AIR 3中的一個新方法,容許您通知垃圾收集器這是完成標記和執行收集的好時機(ActionScript參考文檔中的API項)。計劃在用戶不會注意到時發生可能的暫停,這會帶來更好的用戶體驗。例如,一個遊戲可能在遊戲中一個級別完成時調用此函數,進而減小在玩遊戲期間發生暫停的機會。
您傳遞給此方法的迫近度值用於與垃圾收集器處於標記階段中的位置進行比較。若是您傳遞給它的值比垃圾收集器的迫近度值小,標記和清除將同步完成並致使應用 程序暫停。垃圾收集器必須處於該過程的25%以上,才能響應這個暫停以進行收集的請求。傳遞一個較小的值(但大於0.25)極可能會強制執行收集,致使應 用程序暫停。傳遞一個較大的值將告訴垃圾收集器只有在即將暫停時完成收集。
延伸閱讀
理解內存管理和垃圾收集在Flash Player和AIR中的工做原理,將有助於您優化您的代碼並開發更高性能的應用程序。請查閱Michael Labriola介紹垃圾收集的演示Talking Trash。閱讀Christian Cantrell的Providing Hints to the Garbage Collector in AIR 3。您也能夠閱讀詳細的MMgc討論,其中包含對底層C++代碼的描述。
查看原文:Garbage collection internals for Flash Player and Adobe AIRhtml
轉:http://bbs.5aser.com/forum.php?mod=viewthread&tid=377git