From: Cristian B. <cr...@cs...> - 2014-03-10 17:33:26
|
Hello Makumba is now leak-free on webapp stop/reload/redeploy. On webapp stop/undeploy, all makumba classes and instances disappear nicely from memory. This should have an impact at least in development (e.g. in Parade). This is a different type of leak from webapp "use" leaks (i.e. objects accumulate in memory during use, but are released at webapp reload). Webapp reload/stop may release most webapp objects but still keep some objects (and classes and the webapp classloader), leading to an unusable tomcat after a number of reloads/deploys. Also, in the process, I simplified the singletons. It all started with a compiler warning, that the constructor of the singleton wrapper classes is never used, which was true. That meant that the singleton nullifying mechanism (on webapp stop/reload) that we had in place never took effect. ---- Details ---- Here is the singleton idiom we used: private static class SingletonHolder implements org.makumba.commons.SingletonHolder { private static JspRelationsAnalyzer singleton = new JspRelationsAnalyzer(); public void release() { singleton = null; } public SingletonHolder() { org.makumba.commons.SingletonReleaser.register(this); } } public static JspRelationsAnalyzer getInstance() { return SingletonHolder.singleton; } The compiler warning (in 40+ places) was that the constructor SingletonHolder() is never used... Therefore singletons are never registered to SingletonReleaser, and thus never released. Still I added a finalize() method and saw that the singleton was nicely released, which means that the above mechanism is not necessary! So I simplified the whole thing to private static class SingletonHolder { private static JspRelationsAnalyzer singleton = new JspRelationsAnalyzer(); } This singleton idiom ensures that the singleton is built only when it is first needed. See this for more details: http://c2.com/cgi/wiki?JavaSingleton /* The mechanism also ensures that all the other static fields that the singleton may depend on are initialized by the time the singleton is built... This can be a huge problem, I tried without the inner class and got loads of hard-to-debug ExceptionsInInitializerError because the singleton was built when the other static fields were not yet initialized. */ Singleton release works with these simple static fields because as long as there are no leaked makumba objects, the makumba classes are released on webapp reload/stop, which means that their statics (including singletons) are released. The problem of course comes if at least one makumba object (or class/object that refers a makumba object) remains referenced after webapp stop. In that case, that makumba class will stay in memory, which also means that the webapp classloader (which the classes refer) will stay in memory, with all their static members, etc. Tomcat 6 makes an effort to nullify the static members but that's turned off in tomcat 7. More details: http://zeroturnaround.com/rebellabs/rjc201/ http://wiki.apache.org/tomcat/MemoryLeakProtection In order to check whether the singletons are cleaned out, I used the tomcat 6 leak detection feature http://tomcat.apache.org/tomcat-6.0-doc/manager-howto.html#Finding_memory_leaks Once I saw that my makumba webapp leaks, I used the Eclipse Memory Analyzer to see why. https://www.eclipse.org/mat/ I found that a MakumbaJspFactory object was kept in memory because it was referred by a static field of tomcat's PageContextImpl... The solution I found was to load the PageContextImpl class before makumba sets its JSP factory as default factory, so the static field points to the Tomcat initial JSP factory instead. The other problem I found, which is related to my test webapp, was that one of my classses was using Authenticator.setDefaultAuthenticator(class in the webapp)... That meant that java.net.Authenticator kept a static reference to my object, which had a reference to my class, which had a reference to the webapp classloader, which had references to many other classes, with their big static fields, etc. It is incredibly easy to provoke a webapp classloader leak... The leak-finding tomcat feature is not as good as it seems, it basically performs an aggressive garbage collection and then checks whether the webapp classloader is still in the heap. However the garbage collection may not be good enough, and a leak-free webapp may still be in memory for a while. But when starting tomcat with JAVA_OPTS=-Xmx5M (five megabyte heap), I got consistent answers, without false positives. best, cristi |