GC.collect()对象可达性

时间:2011-04-22 23:58:49

标签: d

没有更多引用的对象不能立即用GC.collect()进行垃圾收集,但是例如中间调用。 new,writeln或Thread.sleep将使GC.collect()可以访问未引用的对象。

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
  退出...

任何人都可以在垃圾收集过程中解释这种对象的可达性吗?

1 个答案:

答案 0 :(得分:6)

我不熟悉D的垃圾收集的细节,但一般的技术是从所有“根指针”开始,找出哪些对象是活的。当事情被编译成机器代码时,这意味着从函数调用堆栈和CPU寄存器开始。

上面的代码可能会编译成类似的东西:

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

当第一个GC.collect()没有对第一个对象的引用时(因为$r0被覆盖)。即使没有使用第二个对象,在$r0中仍然有指向它的指针,因此GC保守地认为它是可达的。请注意,在变量$r0超出范围之后,编译器可以想象清除c2,但这会使代码运行得更慢。

当第一个writeln调用执行时,它可能在内部使用寄存器$r0,因此它清除了对第二个对象的引用。这就是为什么在第二次调用GC.collect()后回收第二个对象。