Az is lehet, hogy az alapokkal van gondom, de jobb későn mint soha.
Adott egy program.
Ennek egy szervlet initjében betöltök egy statikus class egyik statikus metódusával a statikus class statikus változójába értéket (nagy fájlt cachelek).
Mielőtt stop / startot kiadok a tomcat alatt, csak 1 példányt látok a memory dump-ban.
Ha kiadok egy stop / startot, akkor 2 példány lesz a statikus class-ból a memory dumpban. Természetesen mindkettőnek van mérete.
Hogyan lehetne elkerülni ezt? Cachelni muszáj, Tomcat 6 és 7 alatt is próbáltam.
- 2002 megtekintés
Hozzászólások
Attól, hogy cache-elsz, még nem kötelező statikus változóba tenni. Keress, vagy csinálj, valami ojjektumot, aminek van tisztességes életciklus menedzsmentje, és abba tedd.
Amúgy a statikus dolgokat is ki tudja dobni a Java, csak nem biztos, hogy Tomcat-en működik. OSGI alatt működne az is, leglábbis régen teszteltem ilyesmit.
- A hozzászóláshoz be kell jelentkezni
"Amúgy a statikus dolgokat is ki tudja dobni a Java, csak nem biztos, hogy Tomcat-en működik."
Az attól függ, hogy ki töltötte be a class-t. Ha a webapp részeként töltődött be, akkor webapp unload-kor a class-szal együtt repülnek a statikus változói is.
- A hozzászóláshoz be kell jelentkezni
ugye standard beugrokerdes Javas allaskorbe, hogy akkor egy singletonbol hany peldany is lehet a memoriaban? :)
- A hozzászóláshoz be kell jelentkezni
Az attol fugg, hogy van megirva. :-) Ha valahol belul maga az osztaly lepeldanyositja onmagat, akkor ketto.
--
Ki oda vagyik, hol szall a galamb, elszalasztja a kincset itt alant. | Gentoo Portal
- A hozzászóláshoz be kell jelentkezni
Ha Tomcat 7-tel próbálkozol és leállítod a webalkalmazásodat, nem panaszkodik a Tomcat a logjában, hogy memory leakelsz (warning leállítatlan szálakról, kitakarítatlan thread localokról)? Használsz ThreadLocal-t? Heap vagy PermGen kevés? Referálod a cachelt fájlt esetleg máshol is a statikus változón kívül?
- A hozzászóláshoz be kell jelentkezni
1.
Panaszkodik, de nem erre, hanem az EhCache-ra, valamint a psql jdbc driverre:
SEVERE: The web application [] registered the JDBC driver [org.postgresql.Driver] but failed to unregister it when the web application was stopped. To prevent a memory leak, the JDBC Driver has been forcibly unregistered.
2011.03.18. 16:32:50 org.apache.catalina.loader.WebappClassLoader clearReferencesThreads
SEVERE: The web application [] appears to have started a thread named [net.sf.ehcache.CacheManager@d61aef] but has failed to stop it. This is very likely to create a memory leak.
Most átállítottam, hogy build-nál ne vigye át a psql drivert, valamint a classloader-t beállítottam, hogy a contextnél is a system loader legyen, így ezek megszűntek, viszont a 2x deploy még mindig nem megy. Még mindig duplán van ez a class a memóriában. :-(
Most a tomcat manager ezt válaszolja a "find leaks" buttonra:
"No web applications appear to have triggered a memory leak on stop, reload or undeploy."
2.
Nem használok ThreadLocal-t, hacsak valamelyik lib nem.
3.
Heap kevés
FAIL - Encountered exception java.lang.RuntimeException: java.util.concurrent.ExecutionException: java.lang.OutOfMemoryError: Java heap space
4.
Egy másik osztályból is meghívok egy metódust a static classból, ami visszaadja a cache értékét.
Itt esetleg lehet gáz, mivel itt classloaderrel töltöm be dinamikusan az erre hivatkozó osztályt.
Bár, ha nem jut el a betöltésig, akkor is ugyanez a helyzet.
- A hozzászóláshoz be kell jelentkezni
Szerintem nálad egy ilyen trükkösebb memory leak van, ami a class loaderekkel függ össze és igazából nem a static cache az oka, az csak hamarabb előhozza. Erre gyanakodtam az előbb is, de az 1 és 4 pontok megerősítettek.
Javaban classloaderek töltik be a classokat. A classloader referálja az általa betöltött class-okat, a classok is megjegyzik, hogy melyik loader töltötte be őket.
A tomcat minden webalkalmazáshoz létrehoz egy webappclassloader-t és azzal tölti be az osztályokat, amik az adott webapp-pal együtt jönnek (/WEB-INF/classes meg /WEB-INF/lib alatt). Stop-nál a tomcat egyszerűen eldobja a webappclassloader instance-t és így, jól viselkedő webalkalkalmazásnál, a classloader-rel együtt az összes általa betöltött class és az összes statikus változó (amik igazából class változók) felszabadíthatóak. Sajnos van néhány speciális pont (jdbc driver registry, thread local, thread, stb.), amikor a jvm egyéb szolgáltatásai képesek megtartani egy referenciát az egyik webapp-ban betöltött class-ra, vagy változóra (a tomcat ezek egy részét képes észlelni, és egy részét javítani is, ezeket warningolja). Így a következő függőség jön létre: [vmi jvm szintű dolog] -> webappból származó class -> webapp classloader -> összes webappban betöltött class -> ezek statikus változói. Ebben a láncban a tomcat hiába felejti el a webapp classloader-t a lánc fennmarad és az eredmény egy szép memory leak.
Megteheted, hogy a statikus változókat átviszed instance változókba, de ezzel csak átmeneti javulást kapsz, az előbb írt láncból csak a végét csapod le. Ráadásul így valószínűleg már nem heap fog elfogyni, hanem a permgen (mert a class objektumok ott laknak).
Másik lehetőség, hogy meg kell szüntetni minden speciális függést. A memory dumpból látszania kell, hogy ki tartja a class-t vagy a classloader-jét (esetleg használj eclipse mat plugint memory dumpot jól lehet vele elemezni).
Ami a jdbc drivert illeti: álmoskönyv szerint azt a tomcat lib-be kell rakni a webapp libje helyett. De ezt a tomcat orvosolja (törli maga unloadnál).
"valamint a classloader-t beállítottam, hogy a contextnél is a system loader legyen": én nem babrálnám a tomcat class loaderjeit, jól működnek azok alapbeállítások mellett. Úgy kellene megjavítani a webappot, hogy a default beállításokkal működjön jól.
- A hozzászóláshoz be kell jelentkezni
Ok, kiirtottam mindent a servletből, csak az init, van, abban is csak ezt az 1 hívást végzem.
Most elindítom, majd leállítom. Sajnos így is kiakad.
FAIL - Encountered exception java.lang.RuntimeException: java.util.concurrent.ExecutionException: java.lang.OutOfMemoryError: GC overhead limit exceeded
- A hozzászóláshoz be kell jelentkezni
hali! ha debuggolod a jvm-et, pontosabban ha van aktív agent, akkor könnyen előfordulhat, hogy permgent nem szabadít fel.
szerk. oké csak most olvasom a hozzászólásodat, mindesetre akkor csak egy adalék
- A hozzászóláshoz be kell jelentkezni
Úgy néz ki, hogy a hiba mégsem ezen a környéken van, ha kiveszek egy filtert a we.xml-ből, minden normális. Nyomozok tovább...
- A hozzászóláshoz be kell jelentkezni
Úgy néz ki, hogy a filterben a log4j volt a hibás. Átálltam java loggerre és ott rendbejött.
Még van egy gond az EhCache körül is, ha kiveszem ezeket a sorokat, akkor megint jó:
_fCache = EhCacheTools.createCache("fCache", 100);
_aCache = EhCacheTools.createCache("aCache", 100);
- A hozzászóláshoz be kell jelentkezni
Az EhCache-t viszont nem értem.
Itt így jön létre a cache:
_singletonManager = CacheManager.create();
Cache memoryOnlyCache = _singletonManager.getCache(name);
return memoryOnlyCache;
A servlet destroy metódusában pedig biztos, ami biztos meghívom:
CacheManager.getInstance().shutdown();
Ennek ellenére, ha pl. egy ConcurrentHashMap-el helyettesítem, akkor simán tudom használni az alkalmazást, cachel stb. (csak nem törlődik a cacheből semmi), míg a ha berakom, hogy EhCache-t használjon redeploynál kiakad, ha már volt használva a cache.
Ötletek?
- A hozzászóláshoz be kell jelentkezni
Elfelejtettem írni, de közben megoldódott a probléma.
Egyszerűen annyi volt a megoldás, hogy a cache-t nem XML-ből veszem, hanem dinamikusan kreálom.
- A hozzászóláshoz be kell jelentkezni