Утечка памяти без увеличения количества или размера объектов

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

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

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

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

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

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

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

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

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

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

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

3 ответа

Решение

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

Однако в процессе я продемонстрировал, что 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 будет доступно на следующей или двух неделях.

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

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

Надеюсь, что-то здесь полезно.

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

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

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

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


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


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

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