Устранение проблем с утечкой памяти в Java: завершение?

У меня есть неправильное приложение, которое, похоже, течет. После краткого исследования профайлера большая часть памяти (80%) хранится в экземплярах java.lang.ref.Finalizer. Я подозреваю, что финализаторы не работают.

Общей причиной этого является исключение, исключенное из финализатора. Однако javadoc для метода finalize класса Object (см. здесь), кажется, противоречит самому себе: он утверждает

Если нечеткое исключение вызывается методом finalize, исключение игнорируется и завершается завершение этого объекта.

но позже он также утверждает, что

Любое исключение, созданное методом finalize, приводит к остановке завершения этого объекта, но в противном случае игнорируется.

Чему я должен верить (т.е. завершается ли завершение или нет?), и есть ли у вас какие-либо советы о том, как исследовать такие кажущиеся утечки?

Спасибо

Ответы

Ответ 1

Моим первым шагом было бы установить, является ли это настоящей утечкой памяти или нет.

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

У нас было такое же затруднительное положение в моем проекте, и он запускал приложение в режиме "замедленного движения", чтобы выяснить, была ли у нас настоящая утечка. Мы смогли это сделать, замедляя поток входных данных.

Если проблема исчезает при запуске в режиме "замедленного движения", проблема, вероятно, одна из тех, что предложены в предыдущих ответах, то есть поток Finalizer не может обрабатывать очередь финализатора достаточно быстро.

Если это проблема, похоже, вам может понадобиться сделать нетривиальный рефакторинг, как описано на странице , связанной с Bringer128, например

Теперь давайте посмотрим, как писать классы, требующие очистки postmortem, чтобы их пользователи не сталкивались с ранее описанными проблемами. Лучший способ сделать это - разбить такие классы на два - один, чтобы хранить данные, которые требуют очистки postmortem, а другой - держать все остальное - и определять финализатор только на первом

Ответ 2

Оба цитаты говорят:

Исключение приведет к прекращению или завершению завершения этого объекта.

Оба цитаты также говорят:

Неперехваченное исключение игнорируется (т.е. не регистрируется или обрабатывается VM каким-либо образом)

Итак, это отвечает на первую половину вашего вопроса. Я не знаю достаточно о финализаторах, чтобы дать вам совет по отслеживанию утечки памяти.

EDIT: я нашел эту страницу, которая может быть полезной. У него есть советы, такие как установка полей в поле "null" вручную в финализаторах, чтобы позволить GC восстановить их.

EDIT2: Еще несколько интересных ссылок и цитат:

От Анатомия Java Finalizer

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

а также

он не гарантирует, что любые объекты, которые имеют метод finalize(), собирают мусор.

EDIT3: после чтения большей части ссылки Anatomy, кажется, что исключение исключений в потоке Finalizer действительно замедляет ее, почти столько же, сколько вызов Thread.yield(). Вы, кажется, правы, что поток Finalizer в конечном итоге помещает объект как способный быть GC'd, даже если выбрано исключение. Тем не менее, поскольку замедление является значительным, возможно, что в вашем случае поток Finalizer не будет соответствовать скорости создания объекта и выпадающего объема.

Ответ 3

Пункт 7 "Эффективный второй вариант Java:" Избегайте финализаторов". Я настоятельно рекомендую вам прочитать его. Вот выдержка, которая может вам помочь:

"Явные методы завершения обычно используются в сочетании с конструкцией try-finally для обеспечения завершения"

Ответ 4

У меня такая же проблема с вами (ниже картинки). Для нашего случая это связано с тем, что объект имеет wait(0) в своем завершении и никогда не получает уведомление, которое блокирует java.lang.ref.Finalizer $FinalizerThread. Больше ссылок

objects retained by Finalizer

Ответ 5

Я когда-то видел подобную проблему, то есть поток финализатора не может догнать скорость генерации завершаемых объектов.

Мое решение - это сделать замкнутый цикл, используя MemoryMXBean.getObjectPendingFinalizationCount(), PD (пропорциональный и дифференциальный) управляющий алгоритм для управления скоростью, с которой мы генерируем финализируемые объекты, поскольку у нас есть одна запись для ее создания, просто спящий номер секунды с результатом pd algo. он работает хорошо, хотя вам нужно настроить параметр для pd algo.

надеюсь, что это поможет.