预防classloader内存泄漏

最近在开发一个插件动态部署功能,由于在单元测试里批量进行了redeploy,竟然到最后出现了OOM, faint。
看来有必要回顾一下classloader memory leak。

Classloader的内存泄漏。早在JDK1.5以前,就频繁出现于tomcat, jboss的redeploy动作后。
限于当时没有一款像样的内存泄漏分析工具问世。导致,很多用户都不敢轻易使用hotdeploy功能。

随着JDK6的发布,动态化,OSGi等概念也如火如荼地大规模应用于各大app server。使得classloader内存泄漏的问题,再次引起人们的注意。

所谓的classloader内存泄漏,举个简单例子说明一下。
假设有A和B两个classloader,分别加载 A 和 B两个类。
他们new出来的instance,取名叫 a 和 b。此时,如果你通过某种方式,将a的instance传递给b,b又不小心hold住a的实例时,就有可能发生memory leak。
仔细分析,主要是因为a实例hold class A , class A hold classloader A,b hold a,那么b也就跟classload A有了间接引用关系。
GC过程中,如果发现classloader a 从classloader b的引用关系可达(reached),那么classloader A是不会被回收。

这是一个classloader的小常识,多数人都能很好理解。但问题就在于实际应用过程中,classloader的复杂性,往往会让你神不知鬼不觉地跌入memory leak的陷阱。
比如OSGi,你可能会将一些公用的class或lib放到 Share Bundle, 这个bundle是你不希望会被undeploy或很少undeploy的。
不幸的是,随着系统功能逐渐的丰富起来,业务bundle A的某个实例可能稀里糊涂地被 Share bundle hold住了,此时,你又在毫不知情地情况下,尝试了多次redeploy,那memory leak跟你相见只是时间问题。

对于一般应用,我们不需要关注得太深入。但如果你的系统经常使用一些动态化功能,或许就该体检一下了。

一般推荐使用Eclipse MAT做内存分析,从root开始往下找你期望被unloaded的classloader,并祈祷它不要被找到。

推荐下面三篇文档:

http://blogs.sun.com/fkieviet/entry/classloader_leaks_the_dreaded_java
http://blogs.sun.com/fkieviet/entry/how_to_fix_the_dreaded
http://www.zeroturnaround.com/blog/rjc201/

你可能感兴趣的:(eclipse,tomcat,jboss,单元测试,osgi)