Аналізуємо витоку пам'яті в java додатках, використовуючи visualvm

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

Витоку пам'яті можуть бути в купі, області пам'яті використовуваної для зберігання метаінформації, нативной пам'яті.

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

Витік пам'яті в купі

1) Відкрийте програму. Використовуйте останній доступний JDK (JDK 1.6 на даний момент). У нових версіях java істотно покращилися інструменти налагодження. Виконуйте різні дії в занедбаному додатку по кілька разів, щоб визначити, яке з них призводить до витоку пам'яті.

Аналізуємо витоку пам'яті в java додатках, використовуючи visualvm

Досягніть стабільного стану програми. Спостерігайте за розміром купи протягом деякого часу. Якщо «розмір купи після повного складання сміття» (саме повної) кожен раз зростає, це вказує на витік пам'яті. І для цього спостереження ми можемо використовувати VisualVM. Тепер ми визначили операцію, що зумовлює витік пам'яті - значить, якщо ми будемо виконувати цю операцію неодноразово, отримаємо OOM. (Як бути, якщо ми не змогли визначити операцію, що зумовлює витік пам'яті?) Далі ми припускаємо, що ця операція виконується безперервно.

3) Запустіть VisualVM

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

Аналізуємо витоку пам'яті в java додатках, використовуючи visualvm

5) Перейдіть на вкладку Профілювальники. Виберіть прапорець налаштувань для відображення різних опцій. Перейдіть на вкладку налаштувань пам'яті. Виберіть 'record allocations stack traces'. Зніміть прапорець налаштувань - це закриє панель з настройками (всі зміни будуть автоматично збережені).

Аналізуємо витоку пам'яті в java додатках, використовуючи visualvm

6) Тепер натисніть кнопку 'memory' - запуститься профілювання пам'яті. Зачекайте кілька секунд - це займе якийсь час. З'явиться панель з вкладками для різних класів і інформацією про кількість примірників, загальним розміром в байтах.

7) Зачекайте поки стан додатки стабілізується.

8) Зробіть знімок об'єктів, натиснувши кнопку вгорі панелі з вкладками (дивіться скріншот). Отриманий знімок відобразиться в правій частині, а в лівій його мітка.

Аналізуємо витоку пам'яті в java додатках, використовуючи visualvm

9) Почекайте деякий час, щоб стався витік пам'яті. Зробіть знімок також, як і в попередньому кроці. Тепер у нас є два знімка.

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

Аналізуємо витоку пам'яті в java додатках, використовуючи visualvm

11) Знову перейдіть на вкладку Профілювальники. Виберіть певний на попередньому кроці елемент. Клацніть по ньому правою кнопкою мишки і виберіть пункт "Take snapshot and show allocation stack traces '. Згенерує ще один знімок. На цей раз в правій частині вікна також буде відображена вкладка з трасуванням стека. На ній перераховані різні місця, де даний елемент інстанцііруется, а також його процентний внесок у загальне використання пам'яті.

Аналізуємо витоку пам'яті в java додатках, використовуючи visualvm

12) Зачекайте поки станеться ще кілька витоків пам'яті. Зробіть ще кілька знімків.

13) Отримайте дамп купи, використовуваної додатком. Це можна зробити, клацнувши правою кнопкою миші по додатком і вибравши 'heap dump'.

14) Тепер повернемося до відповідних знімків з трасування стека. Порівняйте ці два знімка зі стеками трасування. Визначте методи, які значно відрізняються за вкладом в спільне використання пам'яті. Це місця, в яких інстаціірованіе об'єктів призводить до витоку пам'яті.

Аналізуємо витоку пам'яті в java додатках, використовуючи visualvm

15) Перейдіть до вкладки дампа купи в правій частині. Знайдіть вікно Classes. Двічі клацніть по елементу, визначеному на 10-му кроці. З'являться екземпляри цього класу. Виберіть екземпляр, який ви вважаєте винним у витоку пам'яті (просто інтуїція). Подивіться в правій частині. Розкрийте вікно. Ви побачите об'єкти, що посилаються на ці екземпляри. Тут причина витоку пам'яті.

16) За елементами, певним на 10-му кроці і інформації, отриманої на 14-му і 15-му кроках, ми можемо вирішити проблему з витоком пам'яті. Ми знайшли об'єкт, що приводить до витоку, місце його створення і об'єкти, що посилаються на нього.

Схожі статті