2011-04-22 39 views
2

沒有更多引用的對象不能立即通過GC.collect()進行垃圾收集,但是可以使用GC.collect()的中間調用。 new,writeln或Thread.sleep將使未引用的對象與GC.collect()可達。GC.collect()object reachability

import std.stdio; 
import core.thread; 
import core.memory; 

class C 
{ 
    string m_str; 
    this(string s) {this.m_str = s;} 
    ~this() { writeln("Destructor: ",this.m_str); } 
} 

void main() 
{ 
    { 
     C c1 = new C("c1"); 
    } 
    { 
     C c2 = new C("c2"); 
    } 
    //writeln("Adding this writeln means c2 gets destructed at first GC.collect."); 
    //Thread.sleep(1); // Similarly this call means c2 gets destructed at first GC.collect. 
    //int x=0; for (int i=0; i<1000_000_000;++i) x+=2*i; // Takes time, but does not make c2 get destructed at first GC.collect. 
    GC.collect(); 
    writeln("Running second round GC.collect"); 
    GC.collect(); 
    writeln("Exiting..."); 
} 

上面的代碼返回:

析構函數:C1
運行第二輪GC.Collect的
析構函數:C2
退出...

任何人都可以解釋這一點垃圾收集期間對象的可達性?

+0

忘了提及,上面是在Windows 32位,dmd.2.052沒有編譯器標誌。 – John 2011-04-23 00:09:48

+2

請注意,由於D GC是保守的,它並不能保證任何未被引用的對象將被收集。 – 2011-04-23 07:33:54

回答

6

我並不熟悉D的垃圾收集的細節,但一般的技巧是從所有「根指針」開始計算出哪些對象是活的。將事情編譯成機器碼時,這意味着從函數調用堆棧和CPU寄存器開始。當第一GC.collect()有第一個對象的更多引用(因爲$r0被覆蓋)

$r0 = new C("c1") 
$r0 = new C("c2") 
GC.collect() 
writeln("Running second round GC.collect") 
GC.collect() 
writeln("Exiting...") 

上面的代碼可能向下編譯成類似。即使第二個對象沒有被使用,在$r0中仍然有一個指向它的指針,所以GC保守地認爲它是可達的。請注意,在變量c2超出範圍之後,編譯器可以想象清除$r0,但這會使代碼運行速度變慢。

當第一個writeln調用執行時,它可能在內部使用寄存器$r0,因此它清除對第二個對象的引用。這就是爲什麼在第二次調用GC.collect()後第二個對象被回收的原因。

+0

非常感謝您的回答,在我的後續問題中,關於哪些代碼可以安全地存在於析構函數中是一個先決條件。 – John 2011-04-24 13:34:49