Tomcat如何修复内存泄漏?

5

我正在使用6.0.20版本。我有许多网络应用程序在服务器上运行,随着时间的推移,大约3天左右,服务器需要重新启动,否则服务器会崩溃并变得无响应。

我对JVM的设置如下:

-XX:+HeapDumpOnOutOfMemoryError
-XX:HeapDumpPath=c:\tomcat\Websites\private\mydomain\apache-tomcat-6.0.20\logs

这为我提供了一个hprof文件,我使用Java VisualVM加载它并识别出以下内容:
byte[] 37,206   Instances | Size 86,508,978
int[] 540,909   Instances | Size 55,130,332
char[] 357,847  Instances | Size 41,690,928

列表还有很长,但是我怎么确定是什么导致了这些问题呢?
我正在使用 New Relic 来监控 JVM,只有一个错误似乎一直出现,也就是 org.apache.catalina.connector.ClientAbortException。如果用户会话被中止,那么创建的任何数据库连接或变量是否没有被关闭,从而留下了孤立的对象?
有一个函数在每个 Web 应用程序中广泛使用,不确定它是否对此泄漏有任何影响:
public static String replaceCharacters(String s)
{
    s = s.replaceAll("  ", " ");
    s = s.replaceAll(" ", "_");
    s = s.replaceAll("\351", "e");
    s = s.replaceAll("/", "");
    s = s.replaceAll("--", "-");
    s = s.replaceAll("&", "and");
    s = s.replaceAll("&", "and");
    s = s.replaceAll("__", "_");
    s = s.replaceAll("\\(", "");
    s = s.replaceAll("\\)", "");
    s = s.replaceAll(",", "");
    s = s.replaceAll(":", "");
    s = s.replaceAll("\374", "u");
    s = s.replaceAll("-", "_");
    s = s.replaceAll("\\+", "and");
    s = s.replaceAll("\"", "");
    s = s.replaceAll("\\[", "");
    s = s.replaceAll("\\]", "");
    s = s.replaceAll("\\*", "");
    return s;
}

当用户连接中断时,例如用户关闭浏览器或离开网站,所有变量、连接等是否都会被清除/释放,但是GC不应该处理吗?

以下是我的JVM设置:

-Dcatalina.base=c:\tomcat\Websites\private\mydomain\apache-tomcat-6.0.20
-Dcatalina.home=c:\tomcat\Websites\private\mydomain\apache-tomcat-6.0.20
-Djava.endorsed.dirs=c:\tomcat\Websites\private\mydomain\apache-tomcat-6.0.20\endorsed
-Djava.io.tmpdir=c:\tomcat\Websites\private\mydomain\apache-tomcat-6.0.20\temp
-Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager
-Djava.util.logging.config.file=c:\tomcat\Websites\private\mydomain\apache-tomcat-6.0.20\conf\logging.properties
-Dfile.encoding=UTF-8
-Dsun.jnu.encoding=UTF-8
-javaagent:c:\tomcat\Websites\private\mydomain\apache-tomcat-6.0.20\newrelic\newrelic.jar
-XX:+HeapDumpOnOutOfMemoryError
-XX:HeapDumpPath=c:\tomcat\Websites\private\mydomain\apache-tomcat-6.0.20\logs
-Dcom.sun.management.jmxremote.port=8086
-Dcom.sun.management.jmxremote.ssl=false
-Dcom.sun.management.jmxremote.authenticate=false vfprintf
-Xms1024m
-Xmx1536m

我有什么遗漏吗?服务器有3GB内存。
任何帮助都将不胜感激 :-)

没有简单的方法。你可以尝试做一个dump,然后调用垃圾回收器,再做另一个dump,看看还有什么残留。经典的内存泄漏是由ThreadLocal和不良使用static缓存引起的。你的应用程序是在负载下还是空闲状态下? - Boris the Spider
这是一个实时生产环境,每个应用程序都使用一个类,该类具有以下功能:public static String removeLineBreaks(String s) public static String replace(String s, String s1, String s2) public static String replaceCharacters(String s) - iggyweb
1
通过使用Eclipse Memory Analyzer进行进一步调查,发现两个最大的问题是org.apache.catalina.loader.WebappClassLoader和org.apache.naming.resources.ResourceCache。由于我正在使用Tomcat 6.0.20,我相信WebappClassLoader是一个直到Tomcat 7才得以解决的问题。 - iggyweb
如果您没有多次部署/取消部署,则ClassLoader 不是您的问题。这很重要,因为它引用了您所有的类。如果您正在部署/取消部署,则可能存在ClassLoader泄漏;坏消息是这些非常难以跟踪。 - Boris the Spider
1
更换了JDBC驱动程序后,一切都已经稳定下来了。注意到类正在卸载,总共从今天早上的68个增加到了117个。堆仍在每分钟大约8次之间波动在300到600MB之间。对于23个网站和8个Web应用程序,我猜这还不错。 - iggyweb
3个回答

2
...但是我应该如何确定是什么引起了这些问题呢?
你需要使用一个转储分析器,它可以让你看到是什么使这些对象可达。选择一个对象,查看其他引用它的对象...然后顺着链往回走,直到找到一个"GC根"或者你认识的一些特定应用程序类。
以下是一些关于分析内存快照和内存分析工具的参考资料: 一旦你确定了,你就已经大部分地找到了存储泄漏的源头。
那个函数与泄漏没有直接关系。它肯定不会导致它。(它可能会生成许多垃圾字符串对象...但这是另一个问题。)

我正在使用Java VisualVM查看堆转储。如果我选择int[],则有534,335个实例;如果我选择其中一个实例,则会显示有<500个实例>具有值,但如果我选择其中一个实例,我将无法获得其他信息。我做错了什么吗? - iggyweb
我注意到了与com.mysql.jdbc.NonRegisteringDriver$1.run(NonRegisteringDriver.java:93)相关的Abandoned connection cleanup threads,这是否有关? - iggyweb
我想我可能在这里发现了一些东西,我正在使用mysql-connector-java-5.1.21-bin.jar,我相信它存在一个问题,导致Tomcat无法释放被遗弃的线程。考虑改用mysql-connector-java-5.1.25-bin.jar。 - iggyweb
我将从这里开始,我注意到堆大小在1GB到1.5GB之间,但PermGen大小在55MB到85MB之间,我认为这也需要关注,这会导致GC几乎不断地运行吗?您对PermGen的大小有什么建议吗? - iggyweb
当类变得不可达时,它们将被卸载。但这只会在相关的类加载器变得不可达时发生。如果这是您的问题,您需要了解类加载器内存泄漏的知识。 - Stephen C
显示剩余7条评论

2

我将所有项目迁移到了Tomcat 7.0.42,错误已经消失了,我们的网站更加稳定并且略微更快,我们使用的内存更少,CPU使用率也更好。


0

在本地开发环境中启动服务器,附加分析器(最好使用yourkit),定期进行堆转储。您将看到对象byte[]的增长,并且实际上可以使用此工具将这些byte[]与泄漏应用程序类连接起来,从而帮助您识别代码缺陷。


使用Eclipse Memory Analyzer,我可以看到由于ClientAbortExceptions而产生的byte[]实例,这是用户在页面完全加载之前离开了网页或站点,因此图像没有完全下载。 - iggyweb
你的意思是修复ClientAbortException吗?我需要扩展每个函数来实现,还是可以通过在server.xml文件中声明一个类来全局处理断开连接的请求? - iggyweb

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接