Czas na problem, z którym musi zmierzyć się każdy programista, który często pracuje na kontenerze Apache Tomcat (tutaj dotyczy wersji 6 i 7). Mowa mianowicie o błędzie  java.lang.OutOfMemoryError: PermGen space. Pojawia się on zazwyczaj, gdy w kontenerze mamy uruchomionych kilka rozbudowanych aplikacji i wykonujemy operacje re-deployu bez restartu Tomcata. Nie jest to dobre, należy to rozwiązać, ale z uwagi na złożoność problemu i często niezrozumiałość dla początkujących programistów, problem jest bagatelizowany. Owszem jest nie jest krytyczne rozwiązanie jego (występuje wyłącznie na serwerze deweloperskim) wystarcza twardy restart kontenera aplikacyjnego i „po kłopocie”. Kiedyś przyjdzie jednak zmierzyć się z rozwiązaniem problemu i zaznajomieniem się z terminami pamięci JVM (Java Virtual Machine).

Krótki opis – pamięć JVM

Rodzaje pamięci

  • Eden space – tutaj lądują wszelkie nowo powołane obiekty. Jest to obszar pamięci gdzie pojawiają się zwykle obiekty, o krótkim czasie życia w bardzo dużych ilościach. Jest to bardzo aktywnie wykorzystywany obszar pamięci.
  • From space, To space (survivors space) – to dwa obszary pamięci zajmujące obiekty, które przetrwały parę wczesnych procesów odśmiecania GC. Pełnią one rolę sit dla obiektów, które starzeją się po to, by trafić do tenured space
  • Tenured space – to obszar obiektów „dziadków”. Tu znajdują się obiekty o długim cyklu życia, które trafiają z survivors space.
  • Permanent generation – tu umieszczone są klasy ładowane podczas startu maszyny JVM. Gdy ta pamięć jest przepełniona otrzymujemy PermGen, co kończy się nieodwołalnym zawieszeniem VM.
    GC działa w ten sposób, że bardzo często przegląda pamięć Eden pod kątem obiektów do dealokacji. Nazywa się to częściowym odśmiecaniem. W przypadku, gdy bufory Eden space są całkowicie zapełnione GC zarządza pełne odśmiecanie (co wiąże się z zamrożeniem stanu maszyny) i czyści także obszar obiektów o długim cyklu życia. Gdy tam nie znajdzie miejsca do zagospodarowania to … OutOfMemoryException.

Niemile widziane

  • uruchamianie w bardzo krótkich odstępach czasu pełnego GC (blokowania aplikacji),
  • parametry -Xmx i -Xms różne od siebie – zmusza to GC do zwalniania bloków zajmowanych przez JVM,
  • obszar Edenu zbyt duży (ponieważ odśmiecenie obszaru Edenu będzie pracochłonne dla GC)

PermGen

Pamięć Permgen (Permament Generation Memory). Jest to obszar pamięci, do której ładowane są klasy Javy. Często, gdy korzystając z funkcji otrzymujemy java.lang.OutOfMemoryError: PermGen space, kontener daje nam do zrozumienia, że skończyła mu się pamięć na stosie klas i nie jest w stanie załadować już żadnej kolejnej biblioteki. Niestety ta pamięć nie jest czyszczona podczas ponownego osadzania aplikacji na serwerze i w efekcie co jakiś czas powoduje że serwer przestaje odpowiadać po kolejnym osadzeniu aplikacji.

Warto ustawić dwa parametry (domyślny rozmiar pamięci PermGen oraz maksymalny jej rozmiar) -XX:PermSize=YYYm oraz -XX:MaxPermSize=YYYm na te same wartości by nie męczyć JVM analizą i zwalnianiem bloków pamięci.

Cykl życia obiektów

Nowy obiekt trafia do obszaru pamięci Eden. Obszar ten jest zapychany do pełnego rozmiaru bufora od startu maszyny JVM. W momencie, gdy obszar ten jest pełen, uruchamiany jest proces częściowego odśmiecania. Obiekty, do których znajdują się referencje, kopiowane są do obszaru From Space. Przy drugim częściowym odśmiecaniu obiekty z From Space, do których wciąż istnieją używane referencje, są kopiowane do To Space (na koniec tej operacji From Space jest czyszczone). Przy kolejnym częściowym odśmiecaniu znowu obiekty z To Space są kopiowane do From Space. Cały czas przy tym dodawane są do tych obszarów obiekty z Eden Space. Proces ten działa do momentu, gdy obszary From i To nie są wypełnione w 100%. Wtedy GC przenosi obiekty, które przetrwały wystarczająco dużo częściowych odśmieceń, do obszaru obiektów o długim cyklu życia – Tunered Space.

Przykładowe parametry startowe Tomcata

set JAVA_OPTS=-Xms1024m -Xmx1024m -XX:PermSize=256m -XX:MaxPermSize=256m -XX:MaxNewSize=256m -XX:NewSize=256m -XX:SurvivorRatio=32 -XX:MaxTenuringThreshold=32

Opis zaawansowanych parametrów

Niezalecane ustawianie na serwerze produkcyjnym bez uprzedniego monitorowania działania w określonej konfiguracji.

Narzędzie kontroli przydzielonej pamięci

VisualVM

Poprawe wykresy zajętości buforów

memory_correct_short

 

memory_correct_long