本周的今天专题更具技术性。您可能知道BibSonomy基于MySQL/Tomcat架构。通常BibSonomy运行非常稳定,但Java虚拟机有时会停止运行,并显示“java.lang.OutOfMemoryError:PermGen空间“错误。这主要发生在Tomcat上重新部署BibSonomy项目之后。为什么会发生这种情况?简单的答案是:因为Java虚拟机没有足够的内存用于所谓的永久发电空间。此空间用于在主内存中保存Java类。一个简单的解决方案是为JVM提供更多的PermGen空间。但这并没有解决根本问题。通常JVM有足够的PermGen空间。提供更多内存的唯一结果是:错误将稍晚发生,而不是在重新部署后直接发生。所以我们决定寻找内存泄漏的原因。很快我们发现,web应用程序中有一些类,类加载器无法删除因为它们被“链接”到由标准类加载器加载的类。可能有几个原因为此和使用正确的工具(JDK中的jmap和jhat)加上一些寻找参考链的小程序,我们发现了罪魁祸首:*MySQL连接器/J(请参见http://bugs.mysql.com/bug.php?id=36565)*iBatis公司(请参见https://issues.apache.org/jira/browse/IBATIS-540)*与杰波瑞菲*Tomcat公司(请参见https://issues.apache.org/bugzilla/show_bug.cgi?id=46221)*我们可以通过将一些JAR移动到正确的位置(另请参见在这里和在这里).识别受试者是一项反复的任务——修复一个泄漏导致的下一个泄漏。。。我们一开始并不知道有这么多候选人。我们可以通过切换到新版本来修复iBatis,MySQL、JabRef和Tomcat的修复有点困难。对于JabRef,我们必须修改源代码,使其不会启动AWT。此外,Tomcat生命周期监听器杀死了java.util.参考。文件系统首选项webapp关闭后使用可怕的Java内省攻击:
final Class clazz=CleanupListener.Class.getClassLoader().loadClass(“java.util.prefs.FileSystemPreferences”);
final字段f=clazz.getDeclaredField(“syncTimer”);
f.setAccessible(真);
最终计时器计时器=(计时器)f.get(null);
timer.cancel();
为了修复MySQL错误,侦听器确保在启动web应用程序时,MySQL连接类在web应用程序之前加载,并由标准类加载器加载,这样取消计时器线程(这是泄漏的原因)不会阻止卸载web应用程序。Tomcat中StandardContext的记录器(无论出于何种原因,都是通过webapps类加载器加载的)也会被侦听器杀死。经过几周的工作,我们得到了一个无泄漏的应用程序。糟糕的是,我们正在使用的每个库都可能导致泄漏,如果我们不小心,泄漏会很快恢复。不幸的是,我们没有意识到可以将一种方法放入Tomcat或应用程序中,该方法只检查内存泄漏。希望你发现这个有趣,并祝你自己的应用程序好运。。。