#d
#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.сбор
Деструктора: c2
Завершается…
Кто-нибудь может объяснить эту достижимость объектов во время сборки мусора?
Комментарии:
1. Забыл упомянуть, что вышесказанное доступно в 32-разрядной версии Windows, dmd.2.052, без флагов компилятора.
2. Пожалуйста, обратите внимание, что, поскольку D GC является консервативным, это не гарантирует, что какой-либо объект, на который нет ссылок, когда-либо будет собран.
Ответ №1:
Я не знаком с деталями сборки мусора D, но общий метод заключается в том, чтобы начинать со всех «корневых указателей», чтобы выяснить, какие объекты являются активными. Когда все компилируется вплоть до машинного кода, это означает запуск из стека вызовов функций и регистров процессора.
Приведенный выше код может быть скомпилирован до чего-то вроде:
$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
вызов, он, вероятно, использует register $r0
внутри и поэтому очищает ссылку на второй объект. Вот почему второй объект восстанавливается после второго вызова GC.collect()
.
Комментарии:
1. Большое спасибо за ваш ответ, это было обязательным условием перед моим следующим вопросом о том, какой код может безопасно находиться в деструкторе.