Flash Player垃圾回收機制

Flash Player垃圾回收工作是由垃圾回收器(garbage collector)完成的。垃圾回收器是運行在後臺的一個進程,它釋放那些不再被應用所使用對象所佔用的內存。不再被應用所使用的對象是指那些不再會被那些活動着(工作着)的對象所“引用”的對象。在AS中,對於非基本類型(Boolean, String, Number, uint, int)的對象,在對象之間傳遞的都是對象引用,而不是對象本身。刪除一個變量只是刪除了對象的引用,而不是刪除對象本身。一個對象可以被多處引用,通過這些不同的引用所操作的都是同一個對象。

通過以下兩段代碼可以瞭解基本類型和非基本類型對象的差異:

基本類型的值傳遞:

private function testPrimitiveTypes():void
{
var s1:String="abcd"; //創建了一個新字符串s1,值爲"abcd"
var s2:String=s1; //String是基本類型,所以創建了一個新的字符串s2,s2的值拷貝自s1。
s2+="efg"; //改變s2的值s1不會受影響。
trace("s1:",s1); //輸出abcd
trace("s2:",s2); //輸出abcdefg
var n1:Number=100; //創建一個新的number,值爲100。
var n2:Number=n1; //Number是基本類型,所以又創建一個新number n2,n2的值拷貝自n1。
n2=n2+100; //改變n2對n1不會有任何影響。
trace("n1",n1); //輸出100
trace("n2",n2); //輸出200
}

非基本類型對象的引用傳遞:

private function testNonPrimitiveType():void
{
// 創建一個新對象, 然後將其引用給變量a:
var a:Object = {foo:"bar"}
//將上面所創建對象的引用拷貝給變量b(通過變量b建立對對象的引用):
var b:Object = a;
//刪除變量a中對對象的引用:
delete(a);
// 測試發現對象仍然存在並且被變量b所引用:
trace(b.foo); // 輸出"bar", 所以對象仍然存在
}
對於非基本類型對象,AS3採用兩種方法來判定一個對象是否還有活動的引用,從而決定是否可以將其垃圾回收。一種方法是引用計數法,一種方法是標記清除法。

計數法(Reference Counting

引用計數法是判定對象是否有活動引用的最簡單的一種方法,並且從AS1就開始在Flash中使用。當創建一個對對象的引用後,對象的引用計數就加一,當刪除一個引用時,對象的引用技術就減一。如果對象的引用計數爲0,那麼它被標記爲可被GC(垃圾回收器)
刪除。例如:

var a:Object = {foo:"bar"}
// 現在對象的引用計數爲1(a)
var b:Object = a;
// 現在對象的引用計數爲2(a和b)
delete(a);
// 對象的引用計數又回到了1 (b)
delete(b);
// 對象的引用計數變成0,現在,這個對象可以被GC釋放內存。

引用計數法很簡單,並且不會增加CPU開銷,可惜的是,當出現對象之間循環引用時它就不起作用了。所謂循環引用就是指對象之間直接或者間接地彼此引用,儘管應用已經不再使用這些對象,但是它們的引用計數仍然大於0,因此,這些對象就不會被從內存中移除。請看下面的範例:

//創建第一個對象:
var a:Object = {};
// 創建第二個對象來引用第一個對象:
var b:Object = {foo:a};
//使第一個對象也引用第二個對象:
a.foo = b;
// 刪除兩個活動引用:
delete(a);
delete(b);
上面的例子中兩個活動引用都已被刪除,因此在應用程序中再也無法訪問這兩個對象。但是它們的引用計數都是1,因爲它們彼此相互引用。這種對象間的相互引用可能會更加複雜(a引用b,b引用c,c又引用a,諸如此類),並且難以在代碼中通過刪除引用來使得引用技術爲變爲0。Flash player 6 和7中就因爲XML對象中的循環引用而痛苦,每個XML節點既引用了該節點的子節點,又引用了該節點父節點。因此,這些XML對象永遠不會釋放內存。好在player 8增加了一種新的GC技術,叫做標記清除。

標記清除(Mark Sweeping)

AS3使用的第二種查找不活動對象的GC策略就是標記清除。 Player從應用的根節點開始(在AS3中通常被稱爲”根(root)”),遍歷所有其上的引用,標記每個它所發現的
對象。然後迭代遍歷每個被標記的對象,標記它們的子對象。這個過程第歸進行,直到Player遍歷了應用的整個對象樹並標記了它所發現的每個東西。在這個過程技術的時候,可以安全地認爲,內存中那些沒有被打標記的對象沒有任何活動引用,因此可以被安全地釋放內存。可以通過下圖可以很直觀地瞭解這種機制(綠色的引用在標記清除過程中被遍歷,綠色對象被打上了標記,白色對象將被釋放內存)
標記清除機制非常準確,但是這種方法需要遍歷整個對象結構,因此會增大CPU佔用率。因此,Flash Player9爲了減少這種開銷只是在需要的時候偶爾執行標記清除活動。
注意:上面所說的引用指的是“強引用(strong reference)”,flash player在標記清除過程中會忽略“弱引用(weakness reference )”,也就是說,弱引用在標記清除過程中不被當做引用,不會阻止垃圾回收。
垃圾回收的時機

Flash Player在運行時請求內存的速度受限於瀏覽器。因此,Flash Player採用小量請求大塊內存,而不是大量請求小塊內存的內存請求策略。同樣,Flash Player在運行時釋放內存速度也相對較慢,所以Flash Player會減少釋放內存的次數,只有在必要的時候才釋放內存。也就是說,Flash Player的垃圾回收只有在必要的時候纔會執行。
當前,Flash Player的垃圾回收發生在Flash Player需要另外請求內存之前。這樣,Flash Player可以重新利用垃圾對象所佔用的內存資源,並且可以重新評估需要另外請求的內存數量,也會節省時間。
在程序的實際運行中驗證了以上的說法,並不是每次應用申請內存時都會導致垃圾回收的執行,只有當Flash佔用的內存緊張到一定程度時纔會執行真正的垃圾回收,如果應用中內存開銷增長是勻速的,那麼計算機物理內存越大,則垃圾回收觸發週期越長。在我的測試環境中,計算機有2G的物理內存,直到打開FLSH應用的瀏覽器佔用700M物理內存之後纔會導致Flash Player回收垃圾內存。
來自Adobe公司的Alex Harui 總結了兩點:
1.何時真正執行垃圾回收不
可預知。
2.垃圾回收總是在請求內存的時候觸發,而不是在對象刪除時發生。
最後,有關FP9中垃圾回收一件非常重要的事情就是:垃圾回收不是立即進行的,而是延遲的。當沒有任何對象引用某個對象後,那個對象也不會立即被從內存中清除,相反,它們會在將來某個不確定的時候被移出(從開發者的角度來看)。但這些對象在沒有被垃圾回收之前,它們仍然在工作(佔用內存和CPU),儘管這不是我們所希望的。
雖然我們不能指令Flash Player立即回收內存,但在程序確定不再引用一個正在工作的對象之前,應終止其工作。比如,停止已經啓動的Timer,停止正在播放的視頻或聲音,以防止其繼續佔用CPU。
強制執行垃圾回收的技巧
很多程序員都想能夠在程序中指令計算機進行垃圾回收。目前,Adobe官方沒有公佈相關API能夠強制執行垃圾回收操作。不過互聯網上流傳一種技巧,該方法利用player的bug來實現強制回收內存,主要是通過人爲拋出某種特別的異常會讓Flash Player回收內存,代碼如下:
try
{
var lc1: LocalConnection = new LocalConnection();
var lc2:LocalConnection = new LocalConnection();
lc1.connect( "gcConnection" );
lc2.connect( "gcConnection" );
}
catch (e:Error)
{
}
這種方法強制回收內存並不穩定。因此,在開發應用時不要依賴於這種方法來回收內存,只能將其視爲輔助方法。





發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章