Последствия производительности финализаторов на JVM

Согласно этому сообщению, в .NET,

Финализаторы на самом деле еще хуже, чем это. Помимо этого они работают поздно (который является действительно серьезной проблемой для многих видов ресурсов), они также менее мощны, потому что они могут только выполнить подмножество операций, позволенных в деструкторе (например, финализатор не может надежно использовать другие объекты, тогда как деструктор может), и при записи в том подмножестве, которое финализаторы являются чрезвычайно трудными записать правильно. И сбор finalizable объектов является дорогим: Каждый finalizable объект и потенциально огромный график объектов, достижимых от него, способствуются следующему поколению GC, которое делает более дорогим собраться некоторыми большими несколько.

Это также относится к JVMs в целом и к HotSpot в особенности?

7
задан Alexey Romanov 2 June 2010 в 04:07
поделиться

3 ответа

Вот некоторые избранные цитаты из Effective Java 2nd Edition: Пункт 7: Избегайте финализаторов:

Финализаторы непредсказуемы, часто опасны и вообще не нужны. Их использование может привести к нестабильному поведению, низкой производительности и проблемам переносимости. У финализаторов мало оправданных применений, [...] как правило, следует избегать финализаторов".

Вы должны убедиться, что вам действительно нужны финализаторы; в большинстве случаев они не нужны.

Программистов C++ предупреждают, чтобы они не считали финализаторы аналогом деструкторов C++ в Java. В C++ деструкторы - это обычный способ возврата ресурсов, связанных с объектом, необходимый аналог конструкторов. В Java сборщик мусора возвращает хранилище, связанное с объектом, когда он становится недоступным, не требуя особых усилий со стороны программиста. Деструкторы C++ также используются для возврата других ресурсов, не связанных с памятью. В Java для этой цели обычно используется блок try-finally.

Семантику когда вызываются финализаторы также важно учитывать:

JLS 12.6 Финализация экземпляров классов

Язык программирования Java не определяет, как скоро будет вызван финализатор [...и] какой поток вызовет финализатор для любого данного объекта. [...] Если во время финализации возникает не пойманное исключение, оно игнорируется, и финализация этого объекта завершается. (JLS 12.6.2) Вызовы финализатора не упорядочены

Более того, единственный механизм запуска финализатора по требованию сломан. Следующие цитаты взяты из Effective Java 2nd Edition:

[...] Единственными методами, которые претендуют на гарантию финализации, являются System.runFinalizersOnExit и его злой близнец, Runtime.runFinalizersOnExit. Эти методы фатально ошибочны и были упразднены.

Далее Блох прокомментировал штраф за производительность (выделение его):

О, и вот еще что: существует серьезный штраф за производительность при использовании финализаторов. На моей машине время создания и уничтожения простого объекта составляет около 5.6ns. Добавление финализатора увеличивает это время до 2 400 нс. Другими словами, создание и уничтожение объектов с финализаторами происходит примерно в 430 раз медленнее.

При таком малом количестве деталей о методологии бенчмаркинга я не думаю, что конкретные цифры много значат, но они подтверждают то, что было широко задокументировано: финализаторы очень дороги.

В книге объясняются редкие сценарии, при которых использование финализаторов оправдано. Отсутствие этих цитат в данном ответе является намеренным.

3
ответ дан 7 December 2019 в 05:18
поделиться

В Java есть очень похожий механизм для финализаторов. Вот хорошая статья о финализаторах: http://www.javaworld.com/javaworld/jw-06-1998/jw-06-techniques.html

Общее эмпирическое правило в Java - не использовать их.

1
ответ дан 7 December 2019 в 05:18
поделиться

Вот является явным утверждением от 2004 года:

Объекты с финализаторами (те, которые имеют нетривиальный метод finalize()) имеют значительные накладные расходы по сравнению с объектами без финализаторов и должны использоваться экономно. Завершаемые объекты медленнее распределяются и медленнее собираются. Во время выделения JVM должна зарегистрировать все завершаемые объекты в сборщике мусора, и (по крайней мере, в реализации HotSpot JVM) завершаемые объекты должны следовать более медленному пути выделения, чем большинство других объектов. Точно так же конечные объекты также медленнее собираются. Требуется не менее двух циклов сборки мусора (в лучшем случае), прежде чем завершаемый объект может быть восстановлен, и сборщику мусора приходится выполнять дополнительную работу для вызова финализатора. Результатом является больше времени, затрачиваемого на выделение и сбор объектов, и большая нагрузка на сборщик мусора, поскольку память, используемая недоступными завершаемыми объектами, сохраняется дольше. Объедините это с тем фактом, что финализаторы не гарантируются в какие-либо предсказуемые сроки или даже вообще, и вы увидите, что существует относительно мало ситуаций, для которых финализация является правильным инструментом для использования.

4
ответ дан 7 December 2019 в 05:18
поделиться
Другие вопросы по тегам:

Похожие вопросы: