GC.collect()对象可达性

Joh*_*ohn 2 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...");
}
Run Code Online (Sandbox Code Playgroud)

上面的代码返回:

析构
函数:c1 运行第二轮GC.collect
析构函数:c2
退出...

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

Kan*_*dan 6

我不熟悉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().