Сведения о вопросе

Kimsanov

21:06, 1st October, 2020

Теги

Почему вы не вызываете явно finalize() или не запускаете сборщик мусора?

Просмотров: 530   Ответов: 7

Прочитав этот вопрос, я вспомнил, как меня учили Java и говорили никогда не звонить finalize() и не запускать сборщик мусора, потому что "это большой черный ящик, о котором вам никогда не нужно беспокоиться". Может ли кто-нибудь свести рассуждения об этом к нескольким предложениям? Я уверен, что мог бы прочитать технический отчет Sun по этому вопросу, но я думаю, что хороший, короткий, простой ответ удовлетворил бы мое любопытство.



  Сведения об ответе

ЯЯ__4

00:38, 21st August, 2020

Короткий ответ: Java сбор мусора-это очень тонко настроенный инструмент. System.gc() - это кувалда.

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

Вызов System.gc() напрямую, хотя технически ничего не гарантирует, на практике вызовет дорогую, полную коллекцию кучи stop-the-world. Это почти всегда неправильно . Вы думаете, что экономите ресурсы, но на самом деле тратите их впустую, заставляя Java перепроверять все ваши живые объекты “just in case”.

Если у вас возникли проблемы с GC паузами в критические моменты, вам лучше настроить JVM для использования параллельного сборщика меток/развертки, который был разработан специально для минимизации времени, затраченного на паузу, чем пытаться взять кувалду к проблеме и просто разбить ее дальше.

Документ Sun, о котором вы подумали, находится здесь: Java SE 6 HotSpot™ настройка сборки мусора виртуальных машин

(Еще одна вещь, которую вы можете не знать: реализация метода finalize() на вашем объекте замедляет сборку мусора. Во-первых, потребуется два запуска GC, чтобы собрать объект: один для запуска finalize() и следующий, чтобы убедиться, что объект не был воскрешен во время завершения. Во-вторых, объекты с finalize() методами должны рассматриваться как частные случаи GC, потому что они должны собираться индивидуально, а не просто выбрасываться навалом.)


  Сведения об ответе

PHPH

05:25, 26th August, 2020

Не беспокойтесь о финализаторах.

Переключитесь на инкрементную сборку мусора.

Если вы хотите помочь сборщику мусора, null отключите ссылки на объекты, которые вам больше не нужны. Меньше пути для следования= более явный мусор.

Не забывайте, что (нестатические) внутренние экземпляры класса сохраняют ссылки на свой родительский экземпляр класса. Таким образом, внутренняя классовая нить хранит гораздо больше багажа, чем вы могли бы ожидать.

В очень близком смысле, если вы используете сериализацию и сериализовали временные объекты, вам нужно будет очистить кэш сериализации, вызвав ObjectOutputStream.reset(), или ваш процесс будет пропускать память и в конечном итоге умрет. Недостатком является то, что непереходные объекты будут повторно сериализованы. Сериализация временных результирующих объектов может быть немного более грязной, чем вы думаете!

Рассмотрите возможность использования мягких ссылок. Если вы не знаете, что такое мягкие ссылки, прочтите javadoc для java.lang.ref.SoftReference

Держитесь подальше от фантомных ссылок и слабых ссылок, если вы действительно не возбудимы.

Наконец, если вы действительно не можете терпеть GC использовать в реальном времени Java.

Нет, я не шучу.

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


  Сведения об ответе

appple

16:38, 4th August, 2020

Насколько далеко заходят финализаторы:

  1. Они практически бесполезны. Они не гарантируют, что будут вызваны своевременно или вообще (если GC никогда не запускается, то и никакие финализаторы не будут). Это означает, что вы вообще не должны полагаться на них.
  2. Финализаторы не гарантированно будут идемпотентными. Сборщик мусора очень заботится о том, чтобы гарантировать, что он никогда не вызовет finalize() более одного раза для одного и того же объекта. С хорошо написанными объектами это не будет иметь значения, но с плохо написанными объектами вызов finalize несколько раз может вызвать проблемы (например, двойной выпуск собственного ресурса ... крах).
  3. Каждый объект, имеющий метод finalize() , должен также предоставлять метод close() (или аналогичный). Это та функция, которую вы должны вызывать. например, FileInputStream.close() . Нет никакой причины вызывать finalize() , когда у вас есть более подходящий метод, который должен быть вызван вами.


  Сведения об ответе

screen

19:59, 13th August, 2020

Реальная проблема с закрытием дескрипторов OS в finalize заключается в том, что finalize выполняются без гарантированного порядка. Но если у вас есть дескрипторы к вещам, которые блокируют (например, сокеты), потенциально ваш код может попасть в тупиковую ситуацию (совсем не тривиальную).

Так что я за явное закрытие ручек в предсказуемом порядке. В основном код для работы с ресурсами должен следовать шаблону:

SomeStream s = null;
...
try{
   s = openStream();
   ....
   s.io();
   ...
} finally {
   if (s != null) {
       s.close();
       s = null;
   }
}

Это становится еще более сложным, если вы пишете свои собственные классы, которые работают через JNI и открытые дескрипторы. Вам нужно убедиться, что ручки закрыты (отпущены) и что это произойдет только один раз. Часто забывают OS ручки в настольных платформах J2SE является Graphics[2D] . Даже BufferedImage.getGrpahics() потенциально может вернуть вам дескриптор, который указывает на видеодрайвер (фактически удерживающий ресурс на GPU). Если вы не освободите его самостоятельно и не оставите его сборщику мусора для выполнения этой работы - вы можете обнаружить странную OutOfMemory и подобную ситуацию, когда у вас закончились растровые изображения, отображенные видеокартой, но все еще есть много памяти. По моему опыту, это довольно часто происходит в узких петлях, работающих с графическими объектами (извлечение миниатюр, масштабирование, заточка и т. д.).

В принципе GC не заботится об ответственности программистов за правильное управление ресурсами. Он заботится только о памяти и ни о чем другом. Вызов Stream.finalize close() IMHO был бы лучше реализован, вызывая исключение new RuntimeError ("сбор мусора в потоке, который все еще открыт"). Это позволит сэкономить часы и дни отладки и очистки кода после того, как неаккуратные любители оставили концы теряют.

Счастливого кодирования.

Мир.


  Сведения об ответе

LAST

13:43, 12th August, 2020

Если предположить, что финализаторы похожи на их тезку .NET, то вам действительно нужно вызывать их только тогда, когда у вас есть ресурсы, такие как дескрипторы файлов, которые могут протекать. В большинстве случаев ваши объекты не имеют этих ссылок, поэтому их не нужно вызывать.

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

Если бы вы знали больше о том, что держит внутри себя GC, вы могли бы принимать более обоснованные решения, но тогда вы упустили преимущества GC.


  Сведения об ответе

ITSME

11:25, 4th August, 2020

GC делает большую оптимизацию на том, когда правильно завершить вещи.

Поэтому, если вы не знакомы с тем, как на самом деле работает GC и как он помечает поколения, ручной вызов finalize или start GC'ing, вероятно, повредит производительности, чем поможет.


  Сведения об ответе

#hash

18:58, 21st August, 2020

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

Многие люди используют финализаторы для выполнения таких действий, как закрытие соединений сокетов или удаление временных файлов. Поступая таким образом, Вы делаете свое поведение приложения непредсказуемым и привязанным к тому, когда JVM собирается GC ваш объект. Это может привести к "out of memory" сценариям, но не из-за исчерпания кучи Java, а скорее из-за того, что в системе не хватает дескрипторов для конкретного ресурса.

Еще одна вещь, которую следует иметь в виду, заключается в том, что введение вызовов System.gc() или таких молотков может показать хорошие результаты в вашей среде, но они не обязательно будут транслироваться в другие системы. Не все работают одинаково JVM, есть много, SUN, IBM J9, BEA JRockit, Harmony, OpenJDK и т. д... Это JVM все соответствует JCK (те, которые были официально проверены, что есть), но имеют много свободы, когда дело доходит до быстрого создания вещей. GC - это одна из тех областей, в которые каждый вкладывает значительные средства. Использование молотка часто разрушает это усилие.


Ответить на вопрос

Чтобы ответить на вопрос вам нужно войти в систему или зарегистрироваться