OutOfMemoryError: PermGen

Десь кілька місяців тому виявилося, що в проекті, над яким я працюю, знайшлася потворна бага. Відловити її було неможливо. В будь-який момент працювати з програмою ставало неможливо. В логах вивалювалася помилка OutOfMemoryError: PermGen.

Звичайно, я з цим не вперший раз стикаюся, і вирішив спробувати старий добрий спосіб - прописати параметр до Джава машини -XX:MaxPermSize. Отже, прописавши параметр -XX:MaxPerSize=256m, я запустив JBoss. І що? А нічого. Все одно програма вивалювалася... Так само непердбачувано і неочікувано :(.

Тоді я вирішив спробувати ще раз, але цього разу збільшив память для PermGen space вдвічі: -XX:MaxPerSize=512m. Особливо це мені не допомогло, добитися впевнених 24/7 так і невийшло. І було вирішено, що слід ритися вглубь, не виправляти проблему, а викорінити причину!
Порившись в інтернеті я надибав блог by Frank Kieviet, в якому розповідалося про використання утиліт для Java 6: jmap та jhat.

jmap - дозволяє створювати дампи пам'яті Java програми.
jhat - дозволяє представити інформацію дампа у зручному для користувача вигляді.

Отож, використовуючи jmap я зробив дамп heap пам'яті запущеної програми в процесі її роботи. Я виконав команду

>jmap -dump:format=b,file=dump1 3412

цим самим вказавши, що слід виконати дамп пам'яті програми, зберегти її в бінарному форматі у файлі dump1. 3412 - це pid Java програми, дамп пам'яті якої я робив. Для того, щоб отримати ідентифікатор процесу, у Windows введіть команду tasklist і знайдіть відповідний java.exe процес.

Після того, як в мене вже був збережений у файлі дамп, я скористався програмою jhat. Команда:

>jhat dump1

проаналізувала дамп і запустила веб-сервер, який по-замовчуванню слухає запити на порті 7000. Якщо потрібно буде одночасно запустити декілька jhat, то знайте - адрес порта можна змінити відповідним параметром. Слід також звернути увагу на параметр -J. Всі параметри, які задаються після -J потрапляються напряму до Java машини. В моєму випадку це знадобилося для того, щоб збільшити максимальну межу пам'яті, яку можна виділити. У випадку програми, яка працювала під JBoss AS 4.0.4, я змушений був запускати jhat компандою
>jhat -J-Xmx1536m dump

Після старту jhat, можна спокійно відкривати ваш олюблений броузер, і вводити адрес http://localhost:7000/. Тепер можна переглядати дамп пам'яті програми, використовуючи набір функціоналу, який надається програмою.

Скориставшися цією програмою, я визначив причину моєї проблеми – не всі динамічно генеровані класи вигружалися; декотрі з них так і залишалися назавжди в пам'яті. Оскільки, PermGen Space (Permananent Generated Space) - це простір в пам'яті, в якому знаходяться довгоживучі (або вічноживучі в межах запущеної java-машини) об'єкти, а серед таких є класи та 'interned strings', то зрозуміло, що суть моєї проблеми в тому, що декотрі динамічно генеровані класи продовжували генеруватися, завантажуватися в пам'ять, але навідмінно від інших, вони залишалися там навічно. Рано чи пізно вільна пам'ять завершувалася, і програма уходила в аут із знойним повідомленням "OutOfMemoryError: PermGen".

Але лікування цієї проблеми це вже зовсім інша історія... І наразі вона ще не закінчилася :(.

No comments: