没有更多引用的对象不能立即用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
退出...
任何人都可以在垃圾收集过程中解释这种对象的可达性吗?
答案 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()
后回收第二个对象。