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

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

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

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

но позже, это также заявляет, что

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

Во что я должен верить (т. Е. Остановлена ​​ли финализация или нет?), И есть ли у вас какие-либо советы о том, как исследовать такие очевидные утечки?

Спасибо

5 ответов

Решение

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

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

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

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

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

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

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

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

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

Необработанное исключение игнорируется (то есть не регистрируется и не обрабатывается виртуальной машиной каким-либо образом)

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

РЕДАКТИРОВАТЬ: Я нашел эту страницу, которая может быть полезной. У него есть совет, такой как установка полей в null вручную в финализаторах, чтобы GC мог их вернуть.

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

Из анатомии финализатора Java

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

а также

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

РЕДАКТИРОВАТЬ 3: После прочтения дополнительной ссылки Anatomy, кажется, что создание исключений в потоке Finalizer действительно замедляет его, почти так же, как вызов Thread.yield(). Похоже, вы правы, что поток Финализатора в конечном итоге пометит объект как способный к GC, даже если выдается исключение. Однако, поскольку замедление является значительным, возможно, что в вашем случае поток Finalizer не поспевает за скоростью создания и выпадения объектов из области видимости.

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

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

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

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

Надеюсь, поможет.

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

объекты, оставленные финализатором

Другие вопросы по тегам