没有更多引用的对象不能立即使用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...");
}
Run Code Online (Sandbox Code Playgroud)
上面的代码返回:
析构
函数:c1 运行第二轮GC.collect
析构函数:c2
退出...
任何人都可以在垃圾收集过程中解释这种对象的可达性吗?
我不熟悉D的垃圾收集的细节,但一般的技术是从所有"根指针"开始,找出哪些对象是活的.当事物被编译成机器代码时,这意味着从函数调用堆栈和CPU寄存器开始.
上面的代码可能会编译成如下代码:
$r0 = new C("c1")
$r0 = new C("c2")
GC.collect()
writeln("Running second round GC.collect")
GC.collect()
writeln("Exiting...")
Run Code Online (Sandbox Code Playgroud)
当第一个GC.collect()
没有对第一个对象的引用时(因为$r0
被覆盖).即使没有使用第二个对象,仍然有一个指向它的指针$r0
,所以GC保守地认为它是可达的.请注意,$r0
在变量c2
超出范围之后,编译器可以想象清除,但这会使代码运行得更慢.
当第一个writeln
调用执行时,它可能在$r0
内部使用寄存器,因此它清除了对第二个对象的引用.这就是为什么第二个对象在第二次调用之后被回收的原因GC.collect()
.