Діагностика outofmemoryerror підручними засобами

Один мій колега є адептом філософії "дефолтних налаштувань". Ця філософія пропагує наступний підхід: не намагайтеся змінювати environment під свої потреби, - просто навчитеся користуватися стандартним environment'ом.

Незважаючи на те що сам по собі цей підхід досить суперечливий, в ньому є свої плюси. Уміння вирішувати завдання штатними засобами особливо виручає коли необхідно швидко продіагностувати якусь проблему, а у вас під рукою немає налаштованого environment'а. Наприклад, ви тимчасово працюєте за іншою машиною, або географічно віддалені від вашого милого серцю, прекрасно налаштованого environment'а. Тому, я вважаю, дуже важливо вміти діагностувати типові проблемні ситуації користуючись тільки штатними утилітами. Так що давайте подивимося як ми можемо діагностувати memory leak'і в java, коли у вас під рукою немає нічого крім JDK.

Отже, в логах ви знайшли OutOfMemoryError. Що робити? По-перше, треба усвідомити чого робити не треба. Ні в якому разі не треба перезапускати процес. Зробивши це ви втратите весь heap додатки, а в нашому випадку heap - це єдиний доказ, яка може наштовхнути вас на причини OOM. Вам треба зробити heap dump. Це дозволить зрозуміти хто займає пам'ять, а також чому ця пам'ять не була конкретно вивільнена garbage collector'ом.

Найпростіший спосіб зробити dump - це використовувати утиліту jmap з пакету JDK.

Ось тепер, коли ви зробили dump, ви можете сміливо повертати систему до життя і перезапускати JVM, якщо це потрібно.

Перед доставкою dump'а на вашу машину раджу його перетиснути, так як heap dump'и дуже добре тиснуться. Коли dump буде на вашій машині виникає питання. А яким чином взагалі зрозуміти що там у "не всередині"?

З останніми версіями JDK поставляється додаток VisualVM. яке містить в собі в тому числі і memory profiler. Завантажуємо heap dump в VisualVM і відкриваємо вкладку "Classes".

Діагностика outofmemoryerror підручними засобами

На цій внесок ми бачимо розподіл пам'яті в dump'е за класами об'єктів. В даному випадку більшого всього пам'яті займає тип char []. Мабуть хтось зберігає багато рядків в пам'яті. Хто ж це?

Двічі клацаємо на типі і переходимо в instance view. Тут ми бачимо всі екземпляри даного типу, а також хто на них посилається, а слідчо і то, чому GC їх не зібрав. Переглядаємо кілька примірників.

Діагностика outofmemoryerror підручними засобами

У моєму випадку більшість посилань на рядок утримується базою даних H2 за допомогою soft reference. Трохи погуглити можна дізнатися, що H2 використовує soft reference для зберігання кеша бази даних. Відмінною особливістю soft посилань є те, що JVM збирає їх тільки тоді, коли їй не вистачає пам'яті (перед генерацією OOM). Це робить soft посилання досить зручним механізмом для різного роду кешей. Проте JVM не гарантує що вона встигне зібрати всі soft посилання перед генерацією exception'а. Що, судячи з усього, і відбувається в моєму випадку.

Також варто відзначити, що VisualVM може сам знаходити найближчих GC root, що утримує цей instance від garbage collector'а.

Діагностика outofmemoryerror підручними засобами

Це позбавляє вас від необхідності сайгаків стрибати по дереву referent'ов в пошуках найближчого GC root'а.

Іноді буває так, що причиною OutOfMemoryError служить не брак пам'яті, а інші причини. Наприклад, якщо JVM бачить, що вона витрачає більшу частину процесорного часу на складання сміття, а не на виконання власне додатки, вона генерує наступний exception.

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

Налагодження ускладнюється, - у вас немає heap'а, хоч на кавовій гущі гадай. В цьому випадку, варто перезапустити програму з ключем -XX: -HeapDumpOnOutOfMemoryError. Це змусить JVM зробити heap dump автоматично перед тим як кидати в бідне додаток OOM'ом. Після наступного подібного інциденту у вас з'явиться їжа для роздумів.

Runtime статистика

Ви також можете легко порахувати сумарний об'єм пам'яті займану типами певного пакета.

Хай буде з вами сила дефолтних налаштувань.