Tomcat 修复内存泄漏?
Posted
技术标签:
【中文标题】Tomcat 修复内存泄漏?【英文标题】:Tomcat Fix Memory Leak? 【发布时间】:2013-07-29 12:42:52 【问题描述】:我正在使用 6.0.20 我在服务器上运行了许多 Web 应用程序,随着时间的推移,大约 3 天,服务器需要重新启动,否则服务器会崩溃并无响应。
我对 JVM 有以下设置:
-XX:+HeapDumpOnOutOfMemoryError
-XX:HeapDumpPath=c:\tomcat\Websites\private\mydomain\apache-tomcat-6.0.20\logs
这为我提供了一个我使用 Java VisualVM 加载的 hprof 文件,该文件标识以下内容:
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。客户端中止异常。是否有可能当用户会话中止时,任何创建的数据库连接或变量都没有关闭,因此成为孤立的?
有一个功能在每个网络应用程序中都被大量使用,不确定这是否与泄漏有关:
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 内存。
任何帮助将不胜感激:-)
【问题讨论】:
真的不容易。您可以尝试进行转储,然后调用垃圾收集器,然后再进行转储并查看周围有什么。经典的内存泄漏是由ThreadLocal
和static
缓存的不当使用引起的。您的应用处于负载状态还是空闲状态?
这是一个现场生产环境,每个应用程序使用一个类,该类具有以下功能: public static String removeLineBreaks(String s) public static String replace(String s, String s1, String s2) public static字符串替换字符(字符串 s)
使用 Eclipse Memory Analyzer 进一步调查,两个最大的问题是 org.apache.catalina.loader.WebappClassLoader 和 org.apache.naming.resources.ResourceCache,因为我使用的是 Tomcat 6.0.20,我相信 WebappClassLoader 是一个直到 Tomcat 7 才解决的问题。
如果您没有多次部署/取消部署负载,那么 ClassLoader 不是您的问题。这很大,因为它引用了你所有的类。如果您正在部署/取消部署,那么您可能有 ClassLoader 泄漏;坏消息是这些很难追踪。
更换 JDBC 驱动后,事情已经稳定下来,注意到类正在卸载,卸载的总数从今天早上的 68 个增加到 117 个。堆仍然在大约 300 到 600MB 之间波动。一分钟8次。对于 23 个网站和 8 个网络应用程序,我猜这还不错。
【参考方案1】:
...但是我如何确定导致这些问题的原因?
您需要使用转储分析器来查看使这些对象可访问的原因。选择一个对象,然后查看其他对象或对象引用它...并通过链向后工作,直到找到“GC 根”或您识别的某个特定于应用程序的类。
以下是一些关于分析内存快照和内存分析器的参考资料:
How do I analyze a .hprof file? How to find memory leaks using visualvm Solving OutOfMemoryError - Memory Profilers一旦您确定了这一点,您就已经完成了确定存储泄漏源的大部分工作。
该函数与泄漏没有直接关系。它肯定不会导致它。 (它可能会生成很多垃圾 String 对象......但这是一个不同的问题。)
【讨论】:
我正在使用 Java VisualVM 查看堆转储。如果我选择 int[] 则有 534,335 个实例,如果我选择其中一个实例,它会说有 有一个值,如果我选择其中一个实例,我不会得到其他信息。我做错了吗? 我注意到与 com.mysql.jdbc.NonRegisteringDriver$1.run(NonRegisteringDriver.java:93) 相关的废弃连接清理线程是否相关? 认为我可能在这里偶然发现了一些东西,我正在使用 mysql-connector-java-5.1.21-bin.jar,我认为它存在导致 Tomcat 无法释放废弃线程的问题。考虑改用mysql-connector-java-5.1.25-bin.jar。 这当然是一个合理的解释。尝试一下,看看更改 JAR 是否有帮助。这应该是一个“低风险”的变化。但是,鉴于泄漏对象的数量,我怀疑您发现的并不是唯一的重大泄漏。 我将从这个开始,我注意到堆在 1GB 和 1.5GB 之间,但 PermGen 在 55MB 到 85MB 之间,我认为这也需要注意,这会导致 GC 运行几乎经常?您对 PermGen 的尺寸有什么建议吗?【参考方案2】:我将所有项目迁移到 Tomcat 7.0.42 并且我的错误消失了,我们的网站更加稳定且速度稍快,我们使用的内存更少,cpu 使用率也更好。
【讨论】:
【参考方案3】:在本地开发环境中启动服务器,附加分析器(最好是您的工具包),定期进行堆转储,您将看到对象字节 [] 的增长,您实际上可以使用此工具将这些 byte[]
与您的应用程序类泄漏它这将帮助您识别代码中的缺陷
【讨论】:
使用 Eclipse 内存分析器我可以看到由于 ClientAbortExceptions 导致的 byte[] 实例,用户在页面完全加载之前离开了网页或站点,因此图像尚未完全下载. 您的意思是修复 ClientAbortException 吗?我是否必须扩展每个函数才能这样做,还是可以通过在 server.xml 文件中声明一个类来全局处理断开的请求来完成?以上是关于Tomcat 修复内存泄漏?的主要内容,如果未能解决你的问题,请参考以下文章