2012. augusztus 5., vasárnap

Memory leak

Hát megijedtem, amikor végre úgy tűnt, hogy funkcionálisan hibátlan a program, erre váratlanul bekopogtat egy "OutOfMemoryException: Java heap space" stack trace. Guglizás, mit lehet ezzel kezdeni, mert az első próbálkozásom, ami egyébként tényleg optimalizáló lépés volt, nem kerülte el az újabb OOM-t.

Első tippnek az -Xmx1024m JVM paramétert találtam, ám sem ez, sem 2048-as értékkel turbózott változata nem hozott javulást.

Mindenhol ilyen speckó tool-okról írkálnak, én azokat a végére akartam hagyni, de ezekről beugrott, hogy van a NetBeans-ben egy "Profiler" nevű cucc. Lefuttattam a Memory opcióval. Még ha nem is látom át pontosan, mit csinál, mit ír ki táblázatba, a telemetriából azért láttam, hogy a folyamatosan növekedő heap méret nem túl egészséges.

Aztán véletlen a Google megszánt és kidobott egy "memory leak detection NB Profiler-rel" cikket. Na ebből végre megtudtam, mit is kéne látni a snapshot táblázatban, és hogy kéne onnan továbbmenni, hogy javítási ötletet is kapjak.

A táblázat azt mutatta, hogy a byte[] tömb 4 MB-ot emésztett fel a heap-ből. (Ennyit allokált összesen a futás során, ami nem került felszabadításra, ha jól értem.) Lényeg, hogy ez még ha önmagában kevés is, meg kellene megfékezni, mert arról árulkodik, hogy folyamatosan nő, és nincs felszabadítás. Jobb klikk, "Show Allocations Stack Traces".

Még remekebb táblázat jön fel, azt mutatja, mely osztályok mely eljárásai allokáltak byte[]-ot és pontosan hány bájtot, és kapok hozzá hívási láncot is, tök jó! :-)

Hamar megtaláltam, hogy a MySQL Connector PreparedStatement-je szeret byte tömbökkel dolgozni, és hogy én hol használom ezt az osztályt. És egyből tudtam mit kell tennem: beírni a kifelejtett close() hívásokat a PreparedStatement objektumoknál a használat végén.

Újra lefuttattam a Profiler-t, és megnyugodtam. A heap alig nőtt, azt is nagyon lassan és nagyon kis mértékben, a used heap szépen pattogott le-föl, és a végén a korábbi második helyezett char[] volt a táblázat élén, mint legtöbbet fogyasztó típus.

Most ismét nagy teszt, aztán ha megint elszáll, akkor újabb fejvakarás. :D
(Valószínűleg akkor a char[]-t kell majd kigyomlálnom.)

2 megjegyzés:

Richard O. Legendi írta...

Pedig hányszor emlegettem mindig órákon, hogy amikor kinyitsz egy csatornát/statementet, az első dolog egy close() legyen :-)

Amúgy Java 7 környezet esélytelen? Autoclosable már megy try-with-resources struktúrával.

juzraai írta...

Igen, tudom, szégyellem is magam. :$ Egyszerűen valahogy tök kimaradt a PreparedStatement használata után, pedig korábban írtam egy Statement-et és ott meg odaírtam a close()-t, szóval magamat se értem. :D De ezt most megtanultam, hogy milyen fontos. :) Elég látványos a különbség. :D

Java 7: jó ötlet. Igazából én megszokásból dolgozom 6-os szintax-szal még mindig. :D Viszont most ránéztem a grid-re, ahol futtatnom kell a progit, ott 6-os JRE van felrakva, úgyhogy egyelőre marad így. :)