Возможная утечка памяти в количестве загруженных классов в приложении Java

StackOverflow https://stackoverflow.com/questions/167740

  •  03-07-2019
  •  | 
  •  

Вопрос

Недавно я начал профилировать Java-приложение osgi, которое пишу с использованием VisualVM.Я заметил одну вещь: когда приложение начинает отправлять данные клиенту (через JMS), количество загруженных классов начинает стабильно увеличиваться.Однако размер кучи и размер PermGen остаются постоянными.Количество классов никогда не падает, даже после прекращения отправки данных.Это утечка памяти?Я думаю, что да, потому что загруженные классы должны где-то храниться, однако куча и Permgen никогда не увеличиваются, даже после того, как я запускаю приложение в течение нескольких часов.

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

Это было полезно?

Решение

  

Вы как-то динамически создаете новые классы на лету?

Спасибо за вашу помощь. Я понял, в чем проблема. В одном из моих классов я использовал Jaxb для создания строки XML. При этом JAXB использует отражение для создания нового класса.

JAXBContext context = JAXBContext.newInstance(this.getClass());

Таким образом, хотя JAXBContext не говорил в куче, классы были загружены.

Я снова запустил программу и вижу нормальное плато, как и ожидал.

Другие советы

Я готов поспорить, что ваша проблема связана с генерацией байт-кода.

Многие библиотеки используют CGLib, BCEL, Javasist или Janino, чтобы генерировать байт-код для новых классов во время выполнения, а затем загружать их из управляемого загрузчика классов. Единственный способ освободить эти классы - освободить все ссылки на загрузчик классов.

Поскольку загрузчик классов содержится в каждом классе, это также означает, что вы не должны выпускать ссылки и на все классы [1]. Вы можете поймать их с приличным профилировщиком (я использую Yourkit - поиск нескольких экземпляров загрузчика классов с одинаковым сохраняемым размером)

Одна загвоздка в том, что JVM не выгружает классы по умолчанию (причина в обратной совместимости - люди предполагают (ошибочно), что статические инициализаторы будут выполняться только один раз. Правда в том, что они выполняются каждый раз при загрузке класса .) Чтобы разрешить выгрузку, вы должны передать некоторые из следующих опций:

-XX:+CMSPermGenSweepingEnabled -XX:+CMSClassUnloadingEnabled

(протестировано с JDK 1.5)

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

Смотрите также:

<Ол>
  • http://blogs.oracle.com/watt/resource /jvm-options-list.html
  • http://blogs.oracle.com/jonthecollector/entry/presenting_the_permanent_generation
  • http://forums.sun.com/thread.jspa?messageID=2833028
  • Для понимания этого поведения вам могут пригодиться некоторые флаги горячих точек, например:

    • -XX:+Загрузка трацекласса
    • -XX:+TraceClassВыгрузка

    Это хорошая ссылка:

    http://java.sun.com/javase/technologies/hotspot/vmoptions.jsp

    Если я не пойму неправильно, мы смотрим здесь на загруженные классы, а не на экземпляры.

    Когда ваш код впервые ссылается на класс, JVM заставляет ClassLoader выйти и извлечь информацию о классе из файла .class или подобного.

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

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

    Однако мне кажется странным две вещи:

    <Ол>
  • Почему это так линейно?
  • Почему это не плато?
  • Я ожидаю, что он будет иметь тенденцию к росту, но в виде шаткой линии, а затем будет постепенно увеличиваться, поскольку JVM уже загрузила большинство классов, на которые ссылается ваша программа. Я имею в виду, что в большинстве приложений существует ограниченное число классов.

    Вы как-то динамически создаете новые классы на лету?

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

    Вам нужно выяснить, что это за загружаемые классы.

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

    В более старом коде отношение слушателя вызывает " слушателя " объект, чтобы остаться вокруг. Я бы посмотрел на старые инструменты или те, которые не прошли через много оборотов. Любая давно существующая библиотека, работающая на более позднем JDK, будет знать о ссылочных объектах, что устраняет требование «Удалить прослушиватель».

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

    Не беспокойтесь о слушателях Swing или JDK, все они должны использовать ссылки, так что с вами все будет в порядке.

    Использовать Анализатор памяти Eclipse для проверки дублирования классов и утечек памяти.Может случиться так, что один и тот же класс будет загружен более одного раза.

    С уважением,Маркус

    Лицензировано под: CC-BY-SA с атрибуция
    Не связан с StackOverflow
    scroll top