Як знайти витік пам'яті Java


142

Як ви виявите витік пам'яті на Java (використовуючи, наприклад, JHat)? Я намагався завантажувати кучу сміття в JHat, щоб взяти основний погляд. Однак я не розумію, як я маю змогу знайти кореневу посилання ( ref ) або все, що воно називається. В основному я можу сказати, що є кілька сотень мегабайт записів хеш-таблиць ([java.util.HashMap $ Entry або щось подібне), але карти використовуються всюди ... Чи є спосіб пошуку великих карт чи, можливо, знайти загальні корені великих об’єктних дерев?

[Редагувати] Добре, я читав відповіді поки що, але скажемо просто, що я дешевий сволот (це означає, що мені більше цікаво навчитися користуватися JHat, ніж платити за JProfiler). Крім того, JHat завжди доступний, оскільки він є частиною JDK. Якщо, звичайно, немає ніякого способу з JHat, але грубою силою, але я не можу повірити, що це може бути так.

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


Це чергове «голосування» за JProfiler. Він працює досить добре для аналізу купи, має гідний інтерфейс користувача та працює досить добре. Як каже McKenzieG1, 500 доларів дешевше, ніж час, який ви в іншому випадку спалите, шукаючи джерело для цих витоків. Що стосується ціни на інструменти, це непогано.
joev

Тут є відповідна сторінка Oracle: docs.oracle.com/javase/8/docs/technotes/guides/troubleshoot/…
Laurel

Відповіді:


126

Я використовую наступний підхід до пошуку витоків пам'яті на Java. Я користувався jProfiler з великим успіхом, але вважаю, що будь-який спеціалізований інструмент з графічними можливостями (розрізняються простіше для аналізу в графічній формі) буде працювати.

  1. Запустіть додаток і дочекайтеся, коли він перейде до "стабільного" стану, коли завершиться вся ініціалізація і програма не працює.
  2. Виконайте операцію з підозрою на витік пам'яті кілька разів, щоб дозволити будь-яку кеш-пам'ять, пов’язану з БД.
  3. Запустіть GC і зробіть знімок пам’яті.
  4. Повторіть операцію. Залежно від складності роботи та розмірів даних, які обробляються операцією, можливо, доведеться запускати кілька-багато разів.
  5. Запустіть GC і зробіть знімок пам’яті.
  6. Запустіть діфф для двох знімків та проаналізуйте його.

В основному аналіз повинен починатись з найбільшої позитивної різниці, скажімо, за типом об'єктів і знаходити те, що змушує ці зайві об’єкти залишатися в пам'яті.

Для веб-додатків, які обробляють запити в декількох потоках, аналіз стає складнішим, але, тим не менш, загальний підхід все ще застосовується.

Я зробив чимало проектів, спеціально спрямованих на зменшення пам’яті пам’яті додатків, і цей загальний підхід із деякими налаштуваннями та хитрощами завжди добре працював.


7
Більшість (якщо не всі) профілі Java надають вам можливість викликати GC одним натисканням кнопки. Або ви можете зателефонувати System.gc () з відповідного місця у своєму коді.
Діма Маленко

3
Навіть якщо ми зателефонуємо System.gc (), JVM може вирішити знехтувати викликом. AFAIK - це специфічний JVM. +1 до відповіді.
Анікет Такур

4
Що саме таке "знімок пам'яті"? Чи є щось, що підкаже мені номер кожного типу об'єктів, що працює в моєму коді?
гномився

2
Як перейти від "починати від найбільшої позитивної різниці за типами об'єктів" до "знайти те, що спричиняє ці зайві об'єкти в пам'яті"? Я бачу дуже загальні речі, такі як int [], Object [], string і т.д. Як я можу знайти, звідки вони беруться?
Vituel

48

Опитувач тут, я повинен сказати, що отримання інструменту, який не потребує 5 хвилин, щоб відповісти на будь-який клік, значно полегшує пошук потенційних витоків пам'яті.

Оскільки люди пропонують декілька інструментів (я пробував лише візуальний wm, оскільки я отримав це в пробі JDK та JProbe), я хоч і повинен запропонувати безкоштовний / відкритий інструмент, побудований на платформі Eclipse, аналізатор пам’яті (іноді посилається як на SAP-пам’ять аналізатор) доступний на веб-сайті http://www.eclipse.org/mat/ .

Що насправді класно в цьому інструменті, це те, що він індексував дамп купи, коли я вперше відкрив його, що дозволило йому показувати такі дані, як збережена купа, не чекаючи 5 хвилин для кожного об'єкта (майже всі операції були на тонни швидше, ніж інші інструменти, які я спробував) .

Відкриваючи дамп, на першому екрані відображається кругова діаграма з найбільшими об'єктами (підрахунок затриманої купи), і ви можете швидко перейти до об'єктів, які є великими для комфорту. У ньому також знайдеться ймовірний підозрюваний у витоку, який, я думаю, може стати в нагоді, але оскільки навігації для мене було достатньо, я не дуже вдавався в нього.


1
Варто зауважити: очевидно, в Java 5 і вище HeapDumpOnCtrlBreakпараметр VM недоступний . Я знайшов рішення (поки що шукаю) - використовувати JMap для скидання .hprofфайлу, який я потім вкладаю в Eclipse та використовую MAT для вивчення.
Бен

1
Що стосується отримання купового дампа, більшість профілів (включаючи JVisualVM) включають опцію скидання і купи, і потоків у файл.
bbaja42

13

Інструмент - це велика допомога.

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

У такому випадку це допомагає дізнатись ваш шлях навколо дамп-файлу hprof.

Шукайте САЙТИ ПОЧАТОК. Це показує, які об’єкти використовують найбільше пам'яті. Але об'єкти не згуртовуються виключно за типом: кожен запис містить також ідентифікатор "слід". Потім ви можете шукати це "TRACE nnnn", щоб побачити декілька верхніх кадрів стека, де виділявся об'єкт. Часто, коли я бачу, куди виділяється об’єкт, я виявляю помилку і закінчую. Також зауважте, що ви можете керувати кількістю кадрів, записаних у стеку, за допомогою параметрів -Xrunhprof.

Якщо ви перевірите сайт розподілу, і не бачите нічого поганого, вам доведеться почати зворотне прив’язування від деяких з цих живих об’єктів до кореневих об'єктів, щоб знайти несподіваний ланцюжок посилань. Тут дуже допомагає інструмент, але ви можете зробити те ж саме вручну (ну, з грепом). Існує не один кореневий об'єкт (тобто об'єкт, який не підлягає збору сміття). Нитки, класи та кадри стека виступають як основні об'єкти, і все, на що вони посилаються, не є колекціонуючим.

Щоб зробити ланцюг, подивіться у розділі HEAP DUMP для записів із ідентифікатором невірного сліду. Це переведе вас до запису OBJ або ARR, який показує унікальний ідентифікатор об'єкта у шістнадцятковій формі. Шукайте всі випадки цього ідентифікатора, щоб знайти, хто має чітке посилання на об'єкт. Слідуйте за кожним із цих шляхів назад, коли вони розгалужуються, поки не з’ясуєте, де знаходиться витік. Подивіться, чому інструмент настільки зручний?

Статичні члени є повторним порушником для витоку пам'яті. Насправді навіть без інструменту варто витратити кілька хвилин на перегляд вашого коду для статичних членів карти. Чи може карта зрости великою? Чи що-небудь чистить його записи?


"Купи звалищ є настільки величезним, що виходить з ладу інструмент" - останнє я перевірив, jhatі, MATмабуть, намагаюся завантажити весь дамп купи в пам'ять, і, як правило, збиваються з OutOfMemoryErrorвеликими звалищами (тобто з додатків, які найбільше потребують аналізу купи! ). NetBeans Profiler, схоже, використовує інший алгоритм для індексації посилань, який може бути повільним на великих дампах, але принаймні не споживає необмежену пам'ять в інструменті та виходить з ладу.
Джессі Глік

10

Більшу частину часу у корпоративних програмах відведена купа Java перевищує ідеальний розмір від 12 до 16 ГБ. Мені важко змусити NetBeans-профілера працювати безпосередньо над цими великими програмами Java.

Але зазвичай це не потрібно. Ви можете скористатися утилітою jmap, яка постачається разом з jdk, щоб взяти "живий" відвал купи, тобто jmap скидає купу після запуску GC. Виконайте деяку операцію над програмою, дочекайтеся завершення операції, а потім зробіть ще один "живий" відвал купи. Використовуйте такі інструменти, як Eclipse MAT для завантаження кущів, сортуйте на гістограмі, дивіться, які об’єкти збільшились чи які є найвищими, це дало б підказку.

su  proceeuser
/bin/jmap -dump:live,format=b,file=/tmp/2930javaheap.hrpof 2930(pid of process)

З цим підходом існує лише одна проблема; Величезні породні купи, навіть за умови живого вибору, можуть бути занадто великими, щоб перенести їх на колеги розробки, і може знадобитися машина з достатньою кількістю пам'яті / оперативної пам’яті, щоб відкрити.

Саме тут в картину входить гістограма класу. За допомогою інструменту jmap можна скинути гістограму в реальному класі. Це дасть лише гістограму класу використання пам'яті. В основному у неї не буде інформації для ланцюга посилань. Наприклад, він може поставити масив char вгорі. І клас струн десь внизу. Ви повинні самостійно провести зв’язок.

jdk/jdk1.6.0_38/bin/jmap -histo:live 60030 > /tmp/60030istolive1330.txt

Замість того, щоб взяти два відвали, візьміть дві класи гістограми, як описано вище; Потім порівняйте гістограми класів і побачите класи, які збільшуються. Подивіться, чи можете ви пов’язати класи Java з класами додатків. Це дасть досить хороший натяк. Ось сценарій пітонів, який може допомогти вам порівняти два звалища гістограми jmap. histogramparser.py

Нарешті такі інструменти, як JConolse та VisualVm, є важливими для того, щоб побачити зростання пам’яті з часом та побачити, чи є витік пам’яті. Нарешті, іноді вашою проблемою може бути не витік пам’яті, а високе використання пам’яті. Для цього ввімкніть журнал GC; використовуйте більш досконалий та новий компактний GC, наприклад G1GC; і ви можете використовувати jdk інструменти, такі як jstat, щоб побачити поведінку GC в прямому ефірі

jstat -gccause pid <optional time interval>

Інші референції для google для -jhat, jmap, Full GC, розподіл Humongous, G1GC


1
додав повідомлення в щоденнику із більш детальною інформацією тут - alexpunnen.blogspot.in/2015/06/…
Alex Punnen

5

Є інструменти, які допоможуть вам знайти витоки, як JProbe, YourKit, AD4J або JRockit Mission Control. Останнє - це те, що я особисто найкраще знаю. Будь-який хороший інструмент повинен дозволити вам проаналізувати рівень, коли ви зможете легко визначити, які витоки та де виділяються витікаючі об'єкти.

Використання HashTables, Hashmaps або подібного - один з небагатьох способів, яким ви можете взагалі витік пам'яті в Java. Якби мені довелося знайти витік вручну, я б періодично надрукував розмір своїх HashMaps, а звідти знайду той, куди я додаю елементи, і забуду їх видалити.


4

Що ж, завжди існує низькотехнологічне рішення додавання журналів розміру ваших карт, коли ви змінюєте їх, а потім шукайте в журналах, для яких карти зростають за розумні розміри.



0

Вам дійсно потрібно використовувати профайлер пам'яті, який відстежує розподіли. Погляньте на JProfiler - їхня функція " ходового ходу " чудова, і вони інтегруються з усіма основними Java IDE. Це не безкоштовно, але це не так вже й дорого (499 доларів за одну ліцензію) - ви витратите 500 доларів, що коштують часу, досить швидко, намагаючись знайти витік з менш складними інструментами.


0

Про це можна дізнатися, вимірявши обсяг використання пам'яті після виклику сміттєзбірника кілька разів:

Runtime runtime = Runtime.getRuntime();

while(true) {
    ...
    if(System.currentTimeMillis() % 4000 == 0){
        System.gc();
        float usage = (float) (runtime.totalMemory() - runtime.freeMemory()) / 1024 / 1024;
        System.out.println("Used memory: " + usage + "Mb");
    }

}

Якщо вихідні номери були рівними, у вашій програмі відсутня витік пам’яті, але якщо ви побачили різницю між числами використання пам'яті (зростаючими числами), у вашому проекті є витік пам’яті. Наприклад:

Used memory: 14.603279Mb
Used memory: 14.737213Mb
Used memory: 14.772224Mb
Used memory: 14.802681Mb
Used memory: 14.840599Mb
Used memory: 14.900841Mb
Used memory: 14.942261Mb
Used memory: 14.976143Mb

Зауважте, що іноді потрібен певний час, щоб звільнити пам'ять за допомогою деяких дій, таких як потоки та сокети. Ви не повинні судити за першими результатами, ви повинні перевірити це за певний час.


0

Ознайомтесь із цим екраном на тему пошуку витоків пам'яті за допомогою JProfiler. Це візуальне пояснення відповіді @Dima Malenko.

Примітка. Хоча JProfiler не є безкоштовним, проте пробна версія може вирішувати ситуацію, що склалася.


0

Оскільки більшість із нас використовує Eclipse вже для написання коду, чому б не використовувати інструмент аналізатора пам'яті (MAT) у програмі Eclipse. Це чудово працює.

Затемнення MAT являє собою набір плагінів для IDE Eclipse , який надає інструменти для аналізу heap dumpsз програми Java та визначити memory problemsв додатку.

Це допомагає розробнику знаходити витоки пам’яті з такими функціями

  1. Зйомка знімка пам'яті (Heap Dump)
  2. Гістограма
  3. Затримана купа
  4. Дерево домінаторів
  5. Вивчення шляхів до коренів GC
  6. Інспектор
  7. Поширені анти-візерунки пам’яті
  8. Мова запиту об’єктів

введіть тут опис зображення

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.