Утечка памяти без увеличения количества или размера объектов - PullRequest
23 голосов
/ 16 ноября 2011

В системе IBM iSeries у меня запущена Java-программа - сервер приложений с компонентом веб-сервера, все разработанные внутри компании. При работе на 32-битной или 64-битной JVM J9 (технология IBM для Java) у меня появляются признаки утечки памяти.

Обратите внимание, что проблем с запуском этого программного обеспечения на классической виртуальной машине Java не наблюдается, на нескольких виртуальных машинах Sun / Oracle и на виртуальных машинах Linux. Черт возьми, я регулярно оставляю идентичное программное обеспечение работающим неделями на ноутбуке начального уровня моей жены, пока я работаю над своим веб-сайтом - я могу заверить вас, если у него была утечка памяти, это было бы замечено на этой вещи.

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

enter image description here

Но, если я извлекаю системный дамп JVM при запуске после стабилизации и последующих дампов после того, как выделенная куча значительно выросла, дифференциальное сравнение показывает, что после запуска в течение недели больше нет доступных объектов, чем при запуске. Самый последний, через неделю, показывает 6 дополнительных загруженных классов и несколько объектов, явно связанных с этим. Тщательные обзоры всех живых объектов не показали ничего, что могло бы показаться мне неожиданным.

Я пробовал оптимизированные для пропускной способности и параллельные генераторы мусора.

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

Нет методов, вызываемых JNI (кроме нативного кода, выполняемого как часть основной JVM), и это определенно куча, которая растет - я ясно вижу это в информации IBM WRKJVMJOB, а также в сообщениях с использованием компонентов JMX в моем файле журнала консоли.

Пока что я не могу подключиться к активной JVM с помощью таких инструментов JMX, как JVisualVM, поскольку, хотя сокет прослушивания создается при правильной настройке, соединение отклоняется, по-видимому, на уровне протокола (стек TCP / IP показывает принятый соединение, но JVM его отскакивает).

Я в замешательстве и не знаю, куда идти дальше.

РЕДАКТИРОВАТЬ: просто чтобы уточнить; все эти результаты получены с неинструцированной JVM, потому что я не могу получить доступ JMX к этой JVM (мы работаем над этим с IBM).

РЕДАКТИРОВАТЬ 2011-11-16 19:27: мне удалось получить отчет об активности GC за 1823 цикла GC, который включает в себя конкретные значения для счетчиков Soft / Weak / PhantomReference; в этих цифрах нет признаков безудержного роста. Тем не менее, существует значительный рост в арендуемом пространстве для небольшого объекта (в большом арендуемом пространстве объекта пусто). Он вырос с 9 до 36 млн.

Ответы [ 3 ]

5 голосов
/ 08 декабря 2011

Устранив некоторые неосторожные потери памяти (но не утечки) в моей программе и настроив ГХ лучше для нашей рабочей нагрузки, я довел использование разгружаемой памяти до приемлемого уровня.

Однако в ходе этого процесса я продемонстрировал, что JVM IBM J9, используемая в AS / 400 (он же iSeries, Systemi, i5 и др.), Имеет утечку 1336 байт / мин, что составляет 2 МБ / день. Я могу наблюдать эту утечку с различными программами от «однострочной» тестовой программы вплоть до нашего сервера приложений.

Однострочная тестовая программа:

public class ZMemoryLeak2
extends Object
{

static public synchronized void main(String... args) {
    try { ZMemoryLeak2.class.wait(0); } catch(InterruptedException thr) { System.exit(0); }
    }

}

И отдельная тестовая программа, которая ничего не делала, кроме использования памяти монитора через API JMX, убедительно показала, что 1336 B протекает с интервалом ровно в 1 минуту, никогда не подлежит восстановлению (ну, не восстанавливается после 2 недель работы). OP Примечание: На самом деле это были немного разные суммы для каждого варианта JVM.

Обновление 2012-04-02 : Это было принято IBM как ошибка несколько недель назад; на самом деле он был найден и исправлен в Java 5 примерно в середине прошлого года, и ожидается, что исправление для Java 6 будет доступно в ближайшие неделю или две.

4 голосов
/ 17 ноября 2011

Отличный вопрос.Думал, я бы превратил некоторые из моих комментариев в ответ.

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

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

  3. Я бы посмотрел графики различных разделов памяти.Я подозреваю, что вы видите, как космическое пространство старого поколения поднимается и опускается, а выживший постепенно набирает обороты.Если это правда, что количество объектов не меняется, то @Stephen должен быть прав насчет их внутреннего размера или чего-то еще, что работает.Может быть, по какой-то причине объекту учета не удается сообщить обо всех.

  4. Я обнаружил, что кнопка gc JMX на вкладке памяти выполняет более полный цикл.Это должно быть эквивалентно использованию System.gc(), которое вы пробовали.Только к вашему сведению.

  5. Было бы хорошо включить вывод регистрации GC, чтобы увидеть, видите ли вы какие-либо шаблоны: http://christiansons.net/mike/blog/2008/12/java-garbage-collection-logging/ и http://java.sun.com/developer/technicalArticles/Programming/GCPortal/

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

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

3 голосов
/ 16 ноября 2011

Одним из возможных объяснений является то, что вы видите создание объектов в кеше, реализованном с использованием WeakReference или подобного. Сценарий выглядит так:

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

  • Когда вы делаете снимок, это приводит к запуску полного GC, который (возможно) разрывает ссылки и освобождает кэшированные объекты.

(Обратите внимание на "возможно". Я не уверен, что это объяснение содержит воду ...)


Другое возможное объяснение состоит в том, что ваше приложение имеет такое же количество объектов, но некоторые из них больше. Например, у вас может быть массив некоторого примитивного типа, который вы продолжаете перераспределять с большим размером. Или StringBuilder / StringBuffer, который продолжает расти. Или (в некоторых случаях) ArrayList или аналогичный, который продолжает расти.


Вы знаете, вы могли бы гоняться за призраком здесь. Может случиться так, что системный дамп говорит правду и утечки памяти нет вообще. Вы можете проверить эту теорию, уменьшив размер кучи до такой степени, что реальная утечка памяти может спровоцировать OOME относительно быстро. Если бы я не смог спровоцировать OOME таким образом, я был бы склонен списать это на интересное любопытство ... и перейти к реальной проблеме.

...