Как получить дамп памяти при OutOfMemoryError?
От: York Россия  
Дата: 17.03.06 12:49
Оценка:
Есть у меня на поддержке есть приложение, которое переодически вылетает с OutOfMemoryError. Уже замучился искать причину . Использую JProfiler — и в процессе нормального режима работы вообще не наблюдаю ни каких утечек памяти. Дело в том, что ошибка происходит при каких-то не выясненных условиях, которые не удаётся смоделировать, поэтому интерактивное профилирование приложения не помогает.
Так вот, есть ли какая-нибудь утилита, которая в какой-то момент делает дамп памяти моего приложения в файл, и чтобы потом этот дамп можно было просмотреть? А лучше не в какой-то момент, а в момент генерации OutOfMemoryError (ведь это возможно, т.к. виртуальная машина может оповестить отладчик о том, что сгенерировано исключение).

Сегодня попробовал запускать приложение с параметром -Xrunhprof:file=dump.hprof,format=b, а после этого анализировать содержимое с помощью HAT, но обнаружились следующие недостатки:
... << RSDN@Home 1.1.4 stable SR1 rev. 568>>
Пищальников Юрий
Re: Как получить дамп памяти при OutOfMemoryError?
От: C0s Россия  
Дата: 17.03.06 13:16
Оценка:
Здравствуйте, York, Вы писали:

Y>Есть у меня на поддержке есть приложение, которое переодически вылетает с OutOfMemoryError. Уже замучился искать причину


какая ОС? посмотри на ее общие параметры для процессов

недавно слышал про OutOfMemoryError на хпуксе, полечили (как я понял объяснение) увеличением максимально допустимого размера стека процесса на уровне системы с последующим ее рестартом. ядро вроде пересобирать не пришлось
Re: Как получить дамп памяти при OutOfMemoryError?
От: deepsky Украина  
Дата: 17.03.06 16:59
Оценка: 2 (1)
Здравствуйте, York, Вы писали:

Y>Есть у меня на поддержке есть приложение, которое переодически вылетает с OutOfMemoryError. Уже замучился искать причину . Использую JProfiler — и в процессе нормального режима работы вообще не наблюдаю ни каких утечек памяти. Дело в том, что ошибка происходит при каких-то не выясненных условиях, которые не удаётся смоделировать, поэтому интерактивное профилирование приложения не помогает.

Y>Так вот, есть ли какая-нибудь утилита, которая в какой-то момент делает дамп памяти моего приложения в файл, и чтобы потом этот дамп можно было просмотреть? А лучше не в какой-то момент, а в момент генерации OutOfMemoryError (ведь это возможно, т.к. виртуальная машина может оповестить отладчик о том, что сгенерировано исключение).

здесь
раздел 2.1.1 What does OutOfMemoryError mean?

Может для начала стоит поиграться с Xmx, -XX:+PrintGC, -verbose:gc, -Xloggc:<file> параметрами?
Re[2]: Как получить дамп памяти при OutOfMemoryError?
От: York Россия  
Дата: 17.03.06 18:38
Оценка:
Здравствуйте, C0s, Вы писали:

C0s>какая ОС? посмотри на ее общие параметры для процессов

C0s>недавно слышал про OutOfMemoryError на хпуксе, полечили (как я понял объяснение) увеличением максимально допустимого размера стека процесса на уровне системы с последующим ее рестартом. ядро вроде пересобирать не пришлось

ОС Linux. На какие параметры смотреть?
Не думаю, что проблемы из-за ОС или JVM (Sun 1.4). Сопровождаемое приложение — демон, падает где-то раз в две недели , в нормальном режиме работы ему требуется 9 Мб, максимальный размер кучи — 64 Мб, глубина вызовов не очень большая. Явно где-то в определённых ситуациях остаются какие-то ссылки, но вот какие?!

Об искомом приложении есть такие мысли: моё приложение запускается в отладочном режиме, к нему подсоединяется это приложение, и ставит точку останова на возникновения любого исключения. Как только оно отлавливает OutOfMemoryError, виртуальная моя машина приостанавливается и дамп памяти сливается в файл. IDE умеют ставить брекпоинты на возникновение исключения, IDE и профайлеры умеют приостанавливать JVM, профайлеры умеют сохранять дамп в файл. Вот это бы всё соединить, да ещё чтобы дамп был совместим с форматом какого-нибудь профайлера (JProfiler, YourKit Java Profiler), было бы .
Пищальников Юрий
Re: Как получить дамп памяти при OutOfMemoryError?
От: dshe  
Дата: 20.03.06 08:28
Оценка: 13 (2)
Здравствуйте, York, Вы писали:

Y>Есть у меня на поддержке есть приложение, которое переодически вылетает с OutOfMemoryError. Уже замучился искать причину . Использую JProfiler — и в процессе нормального режима работы вообще не наблюдаю ни каких утечек памяти. Дело в том, что ошибка происходит при каких-то не выясненных условиях, которые не удаётся смоделировать, поэтому интерактивное профилирование приложения не помогает.

Y>Так вот, есть ли какая-нибудь утилита, которая в какой-то момент делает дамп памяти моего приложения в файл, и чтобы потом этот дамп можно было просмотреть? А лучше не в какой-то момент, а в момент генерации OutOfMemoryError (ведь это возможно, т.к. виртуальная машина может оповестить отладчик о том, что сгенерировано исключение).

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

Мне чаще всего встречались такие причины возникновения OutOfMemoryError:

1. банальное выделение слишком большого массива.
Легко обнаруживается, поскольку OutOfMemoryError возникает непосредственно у источника ошибки.
Лечится увеличением доступной памяти (-Xmx) или переписываеним алгоритма таким образом, чтобы память использовалась по меньше.

2. непреднамеренное удержание ссылок в полях (например, как средство примитивного кеширования).
пример,
public class Test {
    private Object reference;

    public void reinitialize() {
        reference = eatHugeAmountOfHeap();
    }
}

OutOfMemoryError обычно возникает при втором вызове reinitialize. Первый вызов проходит нормально, поскольку reference равно null. Но зато второй вызов завершается аварийно поскольку в момент выделения памяти под новое значение, reference продолжает удерживать старое.
Лечится переписываем алгоритма таким образом, чтобы память освобождалась сразу после того, как она перестает быть нужной; явным обнулением ссылки в reinitialize с соответствующим комментарием, чтобы никто это обнуление потом не удалил;
public class Test {
    private Object reference;

    public void reinitialize() {
        // old 'reference' value become unused,
        // and we clear it to prevent
        // unintentional reference holding.
        reference = null;
        reference = eatHugeAmountOfHeap();
    }
}

Иногда можно использовать soft, weak ссылки.
Для профилактики необходимо отказаться от привычки хранить временные значения в полях (а не в локальных переменных) и отказаться от привычки все подряд кешировать (особое внимание следует уделить singleton'ам, особенно тем, которые подписываются на какие-то события)

3. классический memory leak
Наиболее трудно-воспроизводимый баг, поскольку память можеть отъедаться маленькими кусочками втечение продолжительного промежутка времени.
На мой взгляд, наиболее эффективный способ ловли в профайлере:
a) сделать принудительную сборку мусора и сделать первый memory snapshot.
b) выполнить сценарий, который предположительно приводит к утечкам памяти
с) опять сделать принудительную сборку мусора и сделать второй memory snapshot.
d) сравнить оба memory snapshot'а, для того, чтобы отбросить те объекты, которые присутствуют в них обоих. проанализировать те объекты, которые появились во втором. найти пути от root'ов к ним и понять причины этих путей.

Насколько я помню, JProfiler позволяет таким образом обнаруживать memory leak'и. Еще OptimizeIt претендует на "автоматическое" обнаружение утечек памяти (но я полагаю, что он лишь частично автоматизирует обнаружение утечек, и все равно придется думать головой).
--
Дмитро
Re[2]: Как получить дамп памяти при OutOfMemoryError?
От: York Россия  
Дата: 20.03.06 10:10
Оценка:
Здравствуйте, dshe, Вы писали:

D>Я думаю, что ты немного не с того конца подходишь к поиску утечек памяти. Дело в том, что в тот момент, когда возникает OutOfMemoryError в куче находится огромное количество объектов, которые к утечкам памяти не имеют прямого отношения. Расковырять переполненную кучу -- задача не для слабонервных, даже с использованием утилит.


Я уже не знаю, как отловить эту ошибку, поэтому и подумал о таком инструменте. А кучу расковырять я смогу, т.к. уже достаточно хорошо изучил приложение, изучил snapshot'ы JProfiler в нормальном режиме работы приложения, и примерно знаю кол-ва объектов, которые должны быть в куче. И потом расковырять кучу можно так как вы описали ниже: есть снимок в нормальном состоянии и снимок в момент OutOfMemoryError, разница поможет найти лишние объекты. Проблема лишь в том, что снимка в момент OutOfMemoryError нет.

D>1. банальное выделение слишком большого массива.

D>Легко обнаруживается, поскольку OutOfMemoryError возникает непосредственно у источника ошибки.

Может быть и эта причина, но как узнать источник ошибки? Получаемый OutOfMemoryError не имеет трассировки стека.

D>2. непреднамеренное удержание ссылок в полях (например, как средство примитивного кеширования).


Думал о таком варианте, места переинициализации просмотрел уже N раз, вставил освобождения объектов, ошибка так и не исчезла.

D>3. классический memory leak

D>Наиболее трудно-воспроизводимый баг, поскольку память можеть отъедаться маленькими кусочками втечение продолжительного промежутка времени.

Память по немногу не отъедается, по крайней мере в нормальном режиме работы, т.к. к настоящему моменту приложение работает уже 76 часов, а состояние памяти в норме (смотрю в JProfiler), т.е. с точностью до 1 Мб как после запуска (это демон, поэтому пока он не обрабатывает запросы он находится в устойчивом состоянии с примерно фиксированным объёмом занимаемой памяти).

D>На мой взгляд, наиболее эффективный способ ловли в профайлере:

D>a) сделать принудительную сборку мусора и сделать первый memory snapshot.
D>b) выполнить сценарий, который предположительно приводит к утечкам памяти
D>с) опять сделать принудительную сборку мусора и сделать второй memory snapshot.
D>d) сравнить оба memory snapshot'а, для того, чтобы отбросить те объекты, которые присутствуют в них обоих. проанализировать те объекты, которые появились во втором. найти пути от root'ов к ним и понять причины этих путей.

Я где-то уже в этой ветке писал, что не могу воспроизвести сценарий, я не знаю что творится в момент утечки, и что поэтому с помощью профайлера не могу отследить утечки.
... << RSDN@Home 1.1.4 stable SR1 rev. 568>>
Пищальников Юрий
Re[3]: Как получить дамп памяти при OutOfMemoryError?
От: dshe  
Дата: 20.03.06 11:21
Оценка:
Здравствуйте, York, Вы писали:

D>>1. банальное выделение слишком большого массива.

D>>Легко обнаруживается, поскольку OutOfMemoryError возникает непосредственно у источника ошибки.

Y>Может быть и эта причина, но как узнать источник ошибки? Получаемый OutOfMemoryError не имеет трассировки стека.


В debugger'е ставишь breakpoint на OutOfMemoryError, который останавливает все потоки. Когда breakpoint сработает, смотришь стектрейсы всех потоков.

Кстати, однажды я отловил такой баг: из сокета вычитывался пакет данных, который представлял собой 4 байта длины и сами полезные данные. Вычитывался он следующим образом: длина вычитывалась в массив из 4 байт, из них формировался int, выделялся массив такого размера, который мы получили на предыдущем шаге, и далее вычитывались полезные данные. Это прекрасно работало, когда сеть не была нагружена, когда сеть нагружалась, начали появляться странные ошибки. Проблема была в том, что иногда те 4 байта длины вычитывались не целиком, а только 1, 2 или 3 байта. И поэтому размер пакета формировался неправильно (иногда это были очень большие числа). Конечно, если пользоваться java.io.DataInputStream readInt или readFully, таких проблем не будет, но если эту функциональность кто-то делал сам, то вполне возможно, что там будет подобный баг.

D>>3. классический memory leak

D>>Наиболее трудно-воспроизводимый баг, поскольку память можеть отъедаться маленькими кусочками втечение продолжительного промежутка времени.

Y>Память по немногу не отъедается, по крайней мере в нормальном режиме работы, т.к. к настоящему моменту приложение работает уже 76 часов, а состояние памяти в норме (смотрю в JProfiler), т.е. с точностью до 1 Мб как после запуска (это демон, поэтому пока он не обрабатывает запросы он находится в устойчивом состоянии с примерно фиксированным объёмом занимаемой памяти).


Y>Я где-то уже в этой ветке писал, что не могу воспроизвести сценарий, я не знаю что творится в момент утечки, и что поэтому с помощью профайлера не могу отследить утечки.


ну так заставь его обрабатывать запросы. И при этом не обязательно доводить его до OutOfMemoryError (возможно, этого момента придется ждать долго), достаточно лишь обнаружить несанционированное стабильное отъедание.
--
Дмитро
Re[4]: Как получить дамп памяти при OutOfMemoryError?
От: York Россия  
Дата: 21.03.06 10:21
Оценка:
Здравствуйте, dshe, Вы писали:

D>В debugger'е ставишь breakpoint на OutOfMemoryError, который останавливает все потоки. Когда breakpoint сработает, смотришь стектрейсы всех потоков.


Это я сделать и не могу, поэтому и возникла мысль о приложении, которое позволяет сохранить данные о состоянии приложения в момент OutOfMemoryError, не требуя моего присутствия. Демон находится на удалённом сервере и неделями работает нормально. Я могу конечно подцепиться к нему отладчиком, что уже не раз делал, но сидеть с отладчиком и ждать исключения возможности нет.

D> ну так заставь его обрабатывать запросы. И при этом не обязательно доводить его до OutOfMemoryError (возможно, этого момента придется ждать долго), достаточно лишь обнаружить несанционированное стабильное отъедание.


Я уже писал, что не могу смоделировать ситуацию, просто не представляю, что там такого происходит. За всё время наблюдения, ни разу не видел каких-то отклонений в использовании памяти, но в определённые моменты он всё же падает.
... << RSDN@Home 1.1.4 stable SR1 rev. 568>>
Пищальников Юрий
Re: Как получить дамп памяти при OutOfMemoryError?
От: alexdolgin  
Дата: 22.03.06 10:45
Оценка:
Здравствуйте, York, Вы писали:

Y>Есть у меня на поддержке есть приложение, которое переодически вылетает с OutOfMemoryError. Уже замучился искать причину


Попробуй запустить его на jdk1.6, если это возможно.
Вроде, обещают, что там стало получше с диагностикой при OutOfMemoryError.
... << RSDN@Home 1.1.4 stable SR1 rev. 568>>
Re[4]: Как получить дамп памяти при OutOfMemoryError?
От: York Россия  
Дата: 29.03.06 10:58
Оценка: 6 (1)
Здравствуйте, dshe, Вы писали:

D>В debugger'е ставишь breakpoint на OutOfMemoryError, который останавливает все потоки. Когда breakpoint сработает, смотришь стектрейсы всех потоков.


Т.к. демон работает на удалённом сервере, причём по долгу не падая, есть проблемы при использовании отладчиков из IDE. Но недавно я вспомнил про jdb! Можно запустить демона в режиме отладки и подсоединиться к нему с помощью jdb или запустить сначала jdb, а уже из под него демона в отладочном режиме. Я выбрал первый вариант. Вот что я сделал:
  1. Запустил демона в отладочном режиме добавив параметры

    -Xdebug -Xnoagent -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=9999

  2. Создал следующие файлы:
    • jdb.in

      catch OutOfMemoryError
      monitor read jdb.cmd
      monitor

    • jdb.cmd

      suspend
      wherei all
      locals

  3. Запустил отладчик:

    jdb -attach 9999 < jdb.in > jdb.out &

Всё, теперь при OutOfMemoryError в jdb.out я получу stack trace всех потоков и список локальных переменных потока, в котором произошло исключение.
Только есть не решённые проблемы: jdb не завершается, никак не смог этого добиться: добавлял monitor exit в jdb.in, добавлял exit в jdb.cmd — ничего не работает. Поэтому после ошибки, и приложение и отладчик останутся висеть.
Требуемая мне задача так и не решена: дамп памяти я не получил, хотя немного приблизился к определению ошибки, буду думать как применить для этого jdb и какие-нибудь другие утилиты.
... << RSDN@Home 1.1.4 stable SR1 rev. 568>>
Пищальников Юрий
Re: Как получить дамп памяти при OutOfMemoryError?
От: Andrei N.Sobchuck Украина www.smalltalk.ru
Дата: 12.04.06 09:57
Оценка: 6 (1)
Здравствуйте, York, Вы писали:

Y>Сегодня попробовал запускать приложение с параметром -Xrunhprof:file=dump.hprof,format=b, а после этого анализировать содержимое с помощью HAT, но обнаружились следующие недостатки:


Хе-хе. По...морочился недавно с hprof & hat. Побровал как hat так и jhat от 1.6. Он плохо работает не только с логами от 1.4, но и от 1.5 и 1.6 Судя по форумам "читаемость" логов зависит от набора опций переданных в параметр heap. То есть, толи heap=dump, толи heap=site — работает, остальное нет. Однако получить рабочий дамп удалось только от небольшого трёхстрочного "приложения". Создать дамп от tomcat-а уже не удалось. То сама ВМ падает, то лог нечитаемый. Плюс значительно замедлилось время старта приложения, но утверждать, что он замедляет выполнение пока не буду Для себя сделал вывод — hprof в топку.

Нашлась другая опция: -XX:+PrintClassHistogram с ней по SIGQUIT, после списка тредов ВМ печатает инфу такого вида:
num   #instances    #bytes  class name
--------------------------------------
  1:     26865     3353528  <no name>
  2:     26865     1937336  <methodKlass>
  3:     23792     1818744  [C
  4:      2943     1765896  [B
  5:     40036     1541016  <symbolKlass>
  6:      2354     1370752  <constantPoolKlass>
  7:      2354      912952  <instanceKlassKlass>
  8:      2090      874592  <constantPoolCacheKlass>
  9:     30275      726600  java.lang.String
 10:      3542      616528  [I
 11:      5280      422400  java.lang.reflect.Method
 12:      2597      228536  java.lang.Class
 13:      3227      221992  [S
 14:      2254      215296  [Ljava.util.HashMap$Entry;
 15:      3586      171440  [[I
 16:      7095      170280  java.util.HashMap$Entry
 17:      2881      159136  [Ljava.lang.Object;
... и т.д. ...

Можно прикрутить скрипт, который будет периодически получать дамп и ротейтить старые дампы. Авось попадёш на нужный момент.
Опция похоже доступна начиная с 1.4.2 — возможно тебе подойдёт:

Prints the all the java heap objects, their instance count and total space they occupy in the heap. The only downside is that you need to issue a SIGQUIT (see -Xsqnopause) which will leave the app running but will dump all of this data to stdout. Very useful to assist in identifying memory problems for example on a production platform where an CPU intensive profiler cannot be used.

http://www.smalltalk.ru | << RSDN@Home 1.1.4 stable SR1 rev. 568>>
Я ненавижу Hibernate
Автор: Andrei N.Sobchuck
Дата: 08.01.08
!
Re[2]: Как получить дамп памяти при OutOfMemoryError?
От: Andrei N.Sobchuck Украина www.smalltalk.ru
Дата: 10.05.06 10:33
Оценка:
Здравствуйте, Andrei N.Sobchuck, Вы писали:

ANS>Хе-хе. По...морочился недавно с hprof & hat.


Если еще интересно, то нашлась такая штука как Ариадна:

Ariadna Heap Analysis Tool

Ariadna is a tool for analyzing memory leaks. It consists of a low-overhead JVMTI agent for taking a heap snapshot and an analysis tool. It is similar to HAT but its has an almost zero overhead agent and its analyzer should also load faster. It collects less information though.


Требует сборки отдельного агента для ВМ. Собранный агент под винду прилагается. Агент всего 300 строчек на С.

ANS>Нашлась другая опция: -XX:+PrintClassHistogram с ней по SIGQUIT, после списка тредов ВМ печатает инфу такого вида:
http://www.smalltalk.ru | << RSDN@Home 1.1.4 stable SR1 rev. 568>>
Я ненавижу Hibernate
Автор: Andrei N.Sobchuck
Дата: 08.01.08
!
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.