Při práci s Tomcatem jsme narazili na memory leaky. Tato chyba nastává při reloadu aplikace v Tomcatu. Nejčastěji se problém projevuje tak, že se zaplní část paměti heap a PermGen, která je použivaná pro načítané třídy (class).
Když tomcat načítá novou webovou aplikaci, uvolní všechny reference na původní ClassLoader a nechá GC, aby paměť uvolnil. Problém ovšem nastane, pokud na tento ClassLoader stále existuje reference. Pokud třída v rodičovském ClassLoaderu (např. část JDBC přímo v JDK) obsahuje odkaz na třídu v našem původním ClassLoaderu, a tím i přímo na na něj, nastane problém.
Problémy mohou způsobovat např. tyto knihovny:
- JDBC DriverManager
- Jasper (JSP compiler)
- CGLIB – v nové verzi již opraveno
My jsme problém vyřešili tak, že v ostrém provozu Tomcat restartujeme a při vývoji jsme zvýšili paměť pro tomcat – -Xmx a
-XX:MaxPermSize=128m. Tím jsme minimalizovali riziko vzniku problému.
Více informací v článku Memory leak – classloader won’t let go.
Zajímavý článek.
Mám jenom několik dodatků.
1. Co se týká JBosse, tak tento problém existuje také. Takže není pravda, že problémy s integrovaným tomcatem nenastávají. (JBoss 4.0.3 SP1)
2. Někde jsem četl, že problém PermGen způsobují statické instance tříd, které jsou používány ve webových aplikacích – toto ber jenom jako naznak dalšího možného zkoumání. Bylo doporučeno, co nejvíce knihoven nahrát do sdíleného knihovního adresáře tomcata – co nejméně do /WEB-INF/lib.
Snad Ti to nějak pomůže.
Ano, statické prvky to dělají. Ale statické prvky mimo /WEB-INF/lib. Takže jedno řešení je nahrát všechno mimo /WEB-INF/lib, ale i tak některé knihovny budou mít odkaz na objekty v tom waru (např. hibernate a mapované třídy).
Pak je ještě řešení všechno naopak nahrát do /WEB-INF/lib. Ale stejně knihovny z JDK (např. JDBC DriverManager) jsou v původním classpath.
Takže IMHO správným řešením je, aby tomcat uvolnil všechny co je v JDK a jary mít ve /WEB-INF/lib. Pokud ovšem přidáme do globální classpath něco co tomcat nezná a nemůže tak uvolnit, máme opět problémy. Proto mají některé wary různé uvolňovače.
Čím více nad tím přemýšlím, tím více se mi zdá, že způsob jakým se v současné době izolují aplikace v kontejneru není zrovna nejlepší (a těmito slovy tedy potvrzuji tvrzení Arnošta, že se jedná o chybu architektury).
Ještě jeden argument k tomu proč dávat vše mimo /WEB-INF/lib.
Když už nastane leak a classloader se neuvolní (což je skoro vždy), samozřejmě pomůže, když v tomto classloaderu je tříd co nejméně (tím se zmenší velikost memory leaku).