2010-03-24 3 views
27

Мне нужно найти утечку памяти в приложении Java. У меня есть некоторый опыт в этом, но я бы хотел получить совет по методологии/стратегии для этого. Любые рекомендации и рекомендации приветствуются.Метод обнаружения утечки памяти в больших кучах кучи Java

О нашей ситуации:

  1. Heap отвалы больше, чем 1 Гб
  2. У нас есть куча сбрасывают от 5 раз.
  3. У нас нет тестового примера, чтобы спровоцировать это. Это происходит только в (массивной) тестовой среде системы после использования по крайней мере в течение недели.
  4. Система построена на внутренне развитой устаревшей структуре с таким количеством недостатков дизайна, что их невозможно сосчитать.
  5. Никто не понимает рамки в глубину. Он был переведен в один парень в Индии, который почти не отстает от ответа на электронные письма.
  6. Мы сделали свалки кучи памяти с течением времени и пришли к выводу, что с течением времени ни один компонент не увеличивается. Это все, что растет медленно.
  7. Вышеуказанное указывает нам, что это рамки доморощенной системы ORM, которая увеличивает ее использование без ограничений. (Эта система отображает объекты файлы ?! Так что на самом деле не ORM)

Вопрос:Какова методология, которая помогла вам добиться успеха с охотой вниз утечками в масштабе корпоративных приложениях?

+0

Я купил больше памяти – LB40

+0

@LB У нас есть 64 ГБ, но бюджет для этого приложения составляет всего 2 ГБ, и мы не можем разумно увеличить больше пары ГБ, не начав каннибализировать другие подсистемы. –

+0

Кучи кучи специфичны для JVM, поэтому вам нужно использовать инструмент, соответствующий JVM. Это? –

ответ

47

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

Кроме того, вы не можете знать, есть ли что-то утечка или нет, не зная, почему класс в первую очередь.

Я только что провел пару недель, выполняя именно это, и я использовал итеративный процесс.

Во-первых, я обнаружил, что профилирующие кучи в основном бесполезны. Они не могут эффективно анализировать огромные кучи.

Скорее, я полагался почти исключительно на гистограммы jmap.

Я полагаю, что вы знакомы с этим, но для тех, кто не:

jmap -histo:live <pid> > dump.out 

создает гистограмму живой кучи. В двух словах он сообщает вам имена классов и количество экземпляров каждого класса в куче.

Я регулярно выгружаю кучу, каждые 5 минут, 24 часа в сутки. Это может быть слишком сложным для вас, но суть одна и та же.

Я провел несколько различных анализов по этим данным.

Я написал сценарий, чтобы взять две гистограммы и сбросить разницу между ними. Итак, если java.lang.String было 10 в первой дампе, а 15 во втором, мой скрипт выплюнул бы «5 java.lang.String», сказав мне, что он поднялся на 5. Если он упал, число будет отрицательным.

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

Однако некоторые классы сохранены, а другие - GC'd. Эти классы могли легко подниматься и опускаться в целом, но все же течь. Таким образом, они могут выпадать из категории «всегда поднимающихся» классов.

Чтобы найти их, я преобразовал данные в временные ряды и загрузил их в базу данных, Postgres специально. Postgres удобен, потому что он предлагает statistical aggregate functions, поэтому вы можете сделать простые данные linear regression analysis и найти классы, которые растут, даже если они не всегда находятся на вершине диаграмм. Я использовал функцию regr_slope, ища классы с положительным наклоном.

Я нашел этот процесс очень успешным и действительно эффективным. Файлы гистограмм не безумно большие, и их было легко загрузить с хостов. Они не были слишком дороги для запуска в производственной системе (они заставляют большой GC и могут немного заблокировать виртуальную машину). Я запускал это в системе с кучей 2G Java.

Теперь все это может означать потенциально протекающие классы.

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

Например, вы можете обнаружить, что у вас много классов Map.Entry или какой-либо другой системный класс.

Если вы просто не кешируете String, факт состоит в том, что эти системные классы, хотя, возможно, и «нарушители», не являются «проблемой». Если вы кэшируете некоторый класс приложения, класс THAT является лучшим индикатором того, где ваша проблема. Если вы не кэшируете com.app.yourbean, то у вас не будет привязана связанная с ним Map.Entry.

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

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

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

Профилировщик может помочь вам отследить владельцев этого «просочившегося» класса.

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

13

Посмотрите на Eclipse Memory Analyzer. Это отличный инструмент (и автономный, не требует самого Eclipse), который 1) может очень быстро открывать очень большие кучи и 2) имеет некоторые довольно хорошие инструменты автоматического обнаружения. Последнее не идеально, но EMA предоставляет множество действительно хороших способов навигации и запросов объектов на дампе, чтобы найти возможные утечки.

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

+0

Я использовал это, чтобы успешно проанализировать кучу кучи ~ 180 мегаватт только вчера, работает как шарм. – Esko

+0

Eclipse MAT поражает, особенно это детектор утечки памяти. –

+1

Да, это то, что мы в основном используем. Он отлично работает с по меньшей мере 1,5 ГБ кучами на 64-битной Linux (разумеется, бит Win 32 быстро выходит из строя). Единственным недостатком является то, что я не получил очень полезной помощи от автоматизированного анализа. –

1

Если это происходит после использования в неделю, и ваше приложение является как byzantine, как вы описали, возможно, вам лучше перезапустить его каждую неделю?

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

+0

Решение для Windows! (Наш ИТ-отдел использует это для наших серверов Windows) Мы не запускаем систему самостоятельно, она продается компаниям, которые не могут принимать перезагрузки (запланированные или незапланированные). Голый признак нестабильности может вызвать угрозы штрафов. –

+0

Мне это не нравится, но в некоторых сценариях это прагматично. Однако я отмечаю вашу точку зрения о продаже компаниям. –

+0

Я согласен, что это может быть (временное) решение в некоторых случаях. –

3

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

Мы использовали Netbeans некоторое время назад для анализа свалок кучи. Это может быть немного медленным, но это было эффективно. Eclipse просто разбился, и 32-разрядные инструменты Windows тоже.

Если у вас есть доступ к 64-битной системе или системе Linux с 3 ГБ или более, вам будет проще анализировать кучи свалок.

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

Когда все началось? Поговорите с людьми и попробуйте получить некоторую историю. Вы можете заставить кого-то сказать: «Да, именно после того, как они установили XYZ в патче 6.43, у нас получилось странное происшествие».

+0

Мы думали, что это должна быть хорошая идея, но в нашем случае это невозможно в дыре. Чаще всего мы можем выполнять только несколько тестовых случаев. Системный тест выполняется только каждые 6 месяцев или около того, и в последний раз они решили сделать его более интенсивным. После этого мы нашли проблему. Мы попытались понизить рамки и приложение до версии, прошедшей тест раньше. Все три теста не удались, это говорит нам о том, что неисправность - это эфир в другом компоненте системы или долгое время находилась в нашей системе. Другой компонент маловероятен. –

2

У меня был успех с IBM Heap Analyzer. Он предлагает несколько представлений о куче, включая наибольшее падение размера объекта, наиболее часто встречающихся объектов и объектов, отсортированных по размеру.

0

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

+0

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

+0

как еще один анекдот. У меня также были проблемы с загрузкой с jhat и большими (1G) кучами –

+0

какая проблема? проблема с кучей пространства на jvm running jhat? – LB40

5

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

Идея состоит в том, чтобы иметь «график» графика postgres vs.использование памяти каждого класса, нарисовать линию, которая суммирует рост и идентифицировать объекты, которые растут быстрее:

^
    | 
s | Legend: 
i | * - data point 
z | -- - trend 
e | 
( | 
b |     * 
y |      -- 
t |     -- 
e |    * -- * 
s |   -- 
) |  *--  * 
    |  -- * 
    | -- * 
    ---------------------------------------> 
         time 

Преобразования кучи отвалов (нужно несколько) в формат, это удобно для потребления Postgres из кучный формат дамп:

num  #instances   #bytes class name 
---------------------------------------------- 
    1:  4632416  392305928 [C 
    2:  6509258  208296256 java.util.HashMap$Node 
    3:  4615599  110774376 java.lang.String 
    5:   16856  68812488 [B 
    6:  278914  67329632 [Ljava.util.HashMap$Node; 
    7:  1297968  62302464 
... 

Чтобы файл CSV с DateTime в каждом дампе кучи:

2016.09.20 17:33:40,[C,4632416,392305928 
2016.09.20 17:33:40,java.util.HashMap$Node,6509258,208296256 
2016.09.20 17:33:40,java.lang.String,4615599,110774376 
2016.09.20 17:33:40,[B,16856,68812488 
... 

Используя этот скрипт:

# Example invocation: convert.heap.hist.to.csv.pl -f heap.2016.09.20.17.33.40.txt -dt "2016.09.20 17:33:40" >> heap.csv 

my $file; 
my $dt; 
GetOptions (
    "f=s" => \$file, 
    "dt=s" => \$dt 
) or usage("Error in command line arguments"); 
open my $fh, '<', $file or die $!; 

my $last=0; 
my $lastRotation=0; 
while(not eof($fh)) { 
    my $line = <$fh>; 
    $line =~ s/\R//g; #remove newlines 
    # 1:  4442084  369475664 [C 
    my ($instances,$size,$class) = ($line =~ /^\s*\d+:\s+(\d+)\s+(\d+)\s+([\$\[\w\.]+)\s*$/) ; 
    if($instances) { 
     print "$dt,$class,$instances,$size\n"; 
    } 
} 

close($fh); 

Создать таблицу, чтобы поместить данные в

CREATE TABLE heap_histogram (
    histwhen timestamp without time zone NOT NULL, 
    class character varying NOT NULL, 
    instances integer NOT NULL, 
    bytes integer NOT NULL 
); 

Скопируйте данные в новую таблицу

\COPY heap_histogram FROM 'heap.csv' WITH DELIMITER ',' CSV ; 

Запускает отстойный запрос от размера (Num байт) запрос:

SELECT class, REGR_SLOPE(bytes,extract(epoch from histwhen)) as slope 
    FROM public.heap_histogram 
    GROUP BY class 
    HAVING REGR_SLOPE(bytes,extract(epoch from histwhen)) > 0 
    ORDER BY slope DESC 
    ; 

Интерпретировать результаты:

  class    |  slope   
---------------------------+---------------------- 
java.util.ArrayList  |  71.7993806279174 
java.util.HashMap   |  49.0324576155785 
java.lang.String   |  31.7770770326123 
joe.schmoe.BusinessObject |  23.2036817108056 
java.lang.ThreadLocal  |  20.9013528767851 

Наклон байтов добавляется в секунду (так как единица времени находится в секундах). Если вы используете экземпляры вместо размера, то это количество экземпляров, добавленных за секунду.

Моя одна из строк кода, создающая этот joe.schmoe.BusinessObject, отвечала за утечку памяти. Он создавал объект, добавляя его к массиву без проверки, существовал ли он уже. Другие объекты также были созданы вместе с BusinessObject рядом с кодом утечки.

+0

Это в значительной степени то, что я делаю. –

+0

Yup! Моя цель состояла в том, чтобы предоставить все недостающие детали из вашего сообщения. – joseph

Смежные вопросы