Обратите внимание, что если объект можно собрать, это не означает, что он будет фактически собран в любой заданной точке - поэтому ваш метод может выдавать ложные отрицания.Если вызывается метод finalize
какого-либо объекта, вы определенно можете сказать, что он недоступен, но если метод не вызывается, вы не сможете логически вывести что-либо.Как и в большинстве вопросов, связанных с GC, недетерминизм сборщика мусора затрудняет разработку тестов / гарантий о том, что именно он будет делать.
По теме достижимости / собираемости, JLS говорит(12.6.1):
A достижимый объект - это любой объект, к которому можно получить доступ в любом потенциальном продолжающемся вычислении из любого живого потока.Могут быть разработаны оптимизирующие преобразования программы, которые уменьшают число достижимых объектов до меньшего, чем те, которые наивно считаются достижимыми.Например, компилятор или генератор кода может установить переменную или параметр, которые больше не будут использоваться для обнуления, чтобы в будущем хранилище для такого объекта могло быть потенциально восстановимо.
Что болееили не совсем то, что вы ожидаете - я думаю, что приведенный выше абзац изоморфен: «объект недоступен, если вы определенно не будете его больше использовать».
Возвращаясь к исходной ситуации, Можете ли вы подумать о каких-либо практических последствиях между объектом, который считается недостижимым после строки 1, а не строкой 2 ?Моя первоначальная реакция заключается в том, что их нет, и если вам как-то удастся найти такую ситуацию, это, вероятно, будет признаком плохого / искаженного кода, вызывающего виртуальную машину, а не врожденной слабостью языка.
Хотя я открыт для контраргументов.
Редактировать: Спасибо за интересный пример.
Я согласен с вашей оценкой и вижу, куда вы идете, хотя проблема в том,Возможно, более того, что режим отладки слегка меняет семантику вашего кода.
В написанном коде вы присваиваете Timer
локальной переменной, которая впоследствии не читается в пределах ее видимости.Даже самый простой анализ escape может выявить, что переменные timer
не используются где-либо еще в методе main
, и поэтому могут быть исключены.Поэтому я думаю, что ваша первая строка может считаться абсолютно эквивалентной простому вызову конструктора напрямую:
public static void Main (string[] args)
{
new Timer(TimerCallback, null, 0, 1000); // call every second
...
В этом последнем случае ясно, что вновь созданный объект Timer
не доступен сразу после построения (при условии, чточто он не делает ничего хитрого, например добавляет себя в статические поля и т. д. в своем конструкторе);и чтобы он собирался, как только GC дошел до него.
Теперь в случае отладки все немного отличается по той причине, о которой вы упомянули, что разработчик может захотеть проверитьсостояние локальных переменных позже в методе.Поэтому компилятор (и JIT-компилятор) не могут их оптимизировать;это как будто есть доступ к переменной в конце метода, что предотвращает сбор до этой точки.
Несмотря на это, я не думаю, что это фактически меняет семантику.Природа GC заключается в том, что сбор редко гарантируется (по крайней мере, в Java единственная гарантия, которую вы получаете, заключается в том, что если выдается OutOfMemoryError, то все, что считается недоступным, сразу же передается GCed).Фактически, при условии, что у вас было достаточно места в куче для хранения каждого объекта, созданного в течение жизненного цикла, реализация GC без операции является вполне допустимой.Поэтому, хотя вы можете наблюдать поведенческие изменения в количестве тиков Timer
, это нормально, поскольку нет никаких гарантий относительно того, что вы увидите, в зависимости от того, как вы его вызываете.(Концептуально это похоже на то, как таймер, который запускается во время задачи, интенсивно использующей процессор, будет тикать больше раз, когда система находится под нагрузкой - ни один из результатов не является неправильным, поскольку интерфейс не предоставляет такого рода гарантию.)
В этот момент я возвращаю вас к первому предложению этого ответа.:)