Подтвердить что ты не робот

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

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

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

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

enter image description here

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

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

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

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

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

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

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

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

4b9b3361

Ответ 1

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

Однако в этом процессе я продемонстрировал, что IBM J9 JVM, используемая в AS/400 (aka 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); }
    }

}

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

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

Ответ 2

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

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

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

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

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

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

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

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

Ответ 3

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

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

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

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


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


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