2014-02-16 3 views
5

Главный вопрос в теме, но позвольте мне показать мое видение завершения процесса разработки на Java, чтобы я мог попросить вас немного больше.Как объект помечен как завершенный на Java (так что метод finalize не будет вызываться во второй раз)?

Ну, gc начинает сбор мусора, отмечая все живые объекты. Когда все доступные объекты отмечены как «живые». Все остальные объекты недоступны. Следующий шаг - проверить каждый недостижимый объект и определить, можно ли его очистить прямо сейчас, или он должен быть окончательно доработан. gc думает, что следующий путь, если метод finalize объекта имеет тело, то этот объект финализируется и должен быть финализирован; если метод finalize объекта имеет пустое тело (protected void finalize() {}), то он не является окончательным и может быть подметен gc прямо сейчас. (Я прав?)
Все завершаемые объекты будут помещены в ту же очередь, которая будет завершена позже один за другим. Насколько я понимаю, финализируемый объект может потратить много времени на размещение в очереди, ожидая завершения его очереди. Это может произойти из-за того, что обычно только один поток, называемый Finalizer, принимает объекты из очереди и вызывает их метод finalize, и когда у нас есть некоторые трудоемкие операции в методе финализации некоторого объекта, остальные объекты в очереди будут ждать довольно долго, чтобы их можно было доработать. Ну, когда объект был завершен, он помечается как FINALIZED и удаляется из очереди. Во время следующего процесса сбора мусора коллекционер увидит, что этот объект недоступен (снова) и имеет непустой метод финализации (снова), поэтому этот объект должен быть помещен в очередь (снова), но это не произойдет, потому что коллекционер каким-то образом видит, что этот объект был отмечен как FINALIZED. (Это мой главный вопрос: каким образом этот объект был отмечен как FINALIZED, как коллекционер знает, что этот объект не должен быть финализирован снова?)

ответ

5

пока мы говорим о HotSpot JVM ...

Сам объект НЕ отмечен как финализированный.

Каждый раз, когда вы создаете новый объект finalize, JVM создает дополнительный объект FinalizerRef (который несколько похож на ссылки Weak/Soft/Phantom).

Как только ваш объект окажется недоступным с сильными ссылками, обрабатываются специальные ссылки на этот объект. FinalizerRef для вашего объекта будет добавлен в очередь финализатора (который является связанным списком, так же как и с другими ссылочными типами).

Когда поток финализатора потребляет FinalizerRef из очереди, он пустил бы свой нулевой указатель на объект (хотя поток будет поддерживать сильную ссылку на объект до завершения финализатора).

Как только FinalizerRef аннулируется, объект больше не может попасть в очередь финализатора.

BTW

Вы можете видеть время обработки предпочтения (и количество ссылок) в журналах GC с -XX:+PrintReferenceGC (see more GC diagnostic JVM options)

+0

Спасибо. Это именно то, что я искал. Я исследовал эти классы Finalizer и FinalReference, и теперь я ясно понимаю, как это работает –

1

JVM хранит метаданные в заголовке объекта. Любой объект с подклассами finalize() вызывается, даже если пустой. Размещение в очереди не займет много времени, но может ждать в очереди в течение длительного времени.

+0

Я могу поверить, что JVM может изменить заголовок объекта, чтобы пометить его ФИНАЛИЗИРОВАН. Но я проводил некоторое расследование, и я мог видеть ситуацию, когда объект с пустым finalize() был удален сразу после сбора мусора. То, что я пытаюсь сказать, - это если этот объект был помещен в очередь для завершения, потребуется удалить хотя бы две сборки мусора (один gc - поместить объект в очередь, второй gc - удалить объект после он ЗАКЛЮЧЕН). И действительно, когда я добавил System.out.println («Я финализирован») внутри finalize(), этот объект был удален после двух сборщиков мусора. –

+0

@AntonKasyanchuk. Вы можете быть правы, что JVM может сократить этот процесс, но я бы Предположим, это всегда так. –

+1

Спецификация не требует, но настоятельно рекомендует обнаруживать «тривиальные финализаторы», которые включают пустые методы и методы, состоящие исключительно из вызова 'super.finalize()' (заканчивающегося в 'Object.finalize()') ... См. [JLS §12.6. Завершение экземпляров класса] (https://docs.oracle.com/javase/specs/jls/se8/html/jls-12.html#jls-12.6), в конце – Holger

1

Я не знаю, как работает реальный, реализованный процесс финализации, но если бы я должен был это сделать, я бы сделал это таким образом - сохраните флаг tri-state в метаданных объекта, который сообщает GC, если объект просто перестает использоваться, нужен финализатор для запуска или может быть удален. Вы, вероятно, придется проверить источник Java для деталей, но это должно быть общая картина:

(в новом)

object.metadata.is_finalized=NEEDS_FINALIZE; 

(в дс)

while ((object=findUnreachableObject())!=null) { 
    if (object.metadata.is_finalized==NEEDS_FINALIZE) { 
     if (hasNonNullBody(object.finalize)) { 
      Finalizer.addForProcessing(object); 
      object.metadata.is_finalized=IN_FINALIZER_QUEUE; 
     } else { 
      object.metadata.is_finalized=REMOVE_NOW; 
     } 
    } 
    if (object.metadata.is_finalized==REMOVE_NOW) { 
     // destroy the object and free the memory 
    } 
} 

(в Finalizer)

while ((object=getObjectForProcessing)!=null) { 
    object.finalize(); 
    object.metadata.is_finalized=REMOVE_NOW; 
} 
+1

Проблема в том, что нет ничего похожего на 'findUnreachableObject 'в современном GC. Единственное, что вы можете получить, это 'findReachableObject', переместить его в область выживших и просто забыть весь недостижимый мусор. Поскольку большинство новых объектов умирают молодыми, это намного эффективнее. – maaartinus

Смежные вопросы