Tomcat修复内存泄漏?
Tomcat修复内存泄漏?
我使用的是6.0.20版本。我在服务器上运行了许多Web应用程序,随着时间的推移,大约3天后,服务器需要重新启动,否则服务器会崩溃并变得无响应。\n我对JVM进行了以下设置:\n
-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=c:\tomcat\Websites\private\mydomain\apache-tomcat-6.0.20\logs
\n这为我提供了一个hprof文件,我使用Java VisualVM加载该文件后发现以下问题:\n
byte[] 37,206个实例 | 大小 86,508,978 int[] 540,909个实例 | 大小 55,130,332 char[] 357,847个实例 | 大小 41,690,928
\n列表还在继续,但我如何确定是什么导致了这些问题呢?\n我正在使用New Relic监控JVM,只有一个错误似乎出现了,但它是一个经常发生的错误,org.apache.catalina.connector.ClientAbortException。有可能当用户会话中止时,未关闭任何数据库连接或创建的变量,从而导致它们被遗留下来吗?\n每个Web应用程序都会频繁使用一个函数,不确定这是否与内存泄漏有关:\n
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; }
\n有可能当用户连接中止时,例如用户关闭浏览器或用户离开网站时,所有变量、连接等都被清除/释放,但是GC不应该处理这些吗?\n以下是我的JVM设置:\n
-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
\n我有什么遗漏吗?服务器有3GB的内存。\n非常感谢您的帮助 🙂
Tomcat Fix Memory Leak?
最近在本地开发环境中启动服务器,并附加分析器(最好使用YourKit)。定期获取堆转储文件,你会发现对象byte[]的增长,并且你可以通过使用这个工具将这些byte[]
与泄漏它的应用程序类进行连接,从而帮助你识别代码中的缺陷。
使用Eclipse Memory Analyzer,我可以看到由于ClientAbortExceptions而导致的byte[]实例。这是因为用户在页面完全加载之前离开了网页或站点,因此图片还没有完全下载。
你是指修复ClientAbortException吗?我是否需要扩展每个函数来修复它,还是可以通过在server.xml文件中声明一个类来全局处理断开的请求?
Tomcat Fix Memory Leak?
在使用Tomcat时,有一个内存泄漏的问题,但是如何确定是什么原因造成了这个问题呢?你需要使用一个内存分析工具来查看是什么让这些对象仍然可达。选择一个对象,查看是哪个其他对象引用了它...然后沿着链条向后查找,直到找到一个"GC root"或者你认识的一些特定的应用类。
这里有一些关于分析内存快照和内存分析工具的参考资料:
- [如何分析.hprof文件?](https://stackoverflow.com/questions/185893)
- [如何使用visualvm查找内存泄漏](https://stackoverflow.com/questions/9154785)
- [解决OutOfMemoryError-内存分析工具](http://plumbr.eu/blog/solving-outofmemoryerror-memory-profilers)
一旦你确定了原因,你就已经基本上找到了存储泄漏的来源。
那个函数与泄漏没有直接关系。它肯定不会引起泄漏。(它可能会生成大量的垃圾String对象...但这是一个不同的问题。)
我正在使用Java VisualVM来查看堆转储。如果我选择int[],有534,335个实例,如果我选择其中一个实例,它会显示有<500个实例>有一个值,如果我选择其中一个实例,就没有其他信息。我做错了什么吗?
我注意到了与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的大小有什么建议吗?
我已经移除了mysql-connector-java-5.1.21-bin.jar并替换为mysql-connector-java-5.1.25-bin.jar,将-XX:PermSize=512m和-XX:MaxPermSize=512m添加到JVM设置中,只是想知道是否需要添加-XX:+CMSClassUnloadingEnabled -XX:+CMSPermGenSweepingEnabled -XX:+UseConcMarkSweepGC。
除非你遇到了说你的permgen内存不足的OOME异常,否则你不需要调整permgen。除非你在进行热部署,否则permgen的水平应该是稳定的。不要同时更改很多东西...因为如果你这样做,你将无法弄清楚到底是什么产生了差异。调整GC参数不会解决存储泄漏的问题。请不要改变它们。
"PermGen在55MB到85MB之间,...这是否会导致GC几乎不停地运行?" 可能不会,但这取决于实际分配permgen的是什么。(热部署?)
谢谢你的建议,我最好移除-XX:PermSize=512m和-XX:MaxPermSize=512m这些行吗?我可以看到堆在300MB到600MB之间波动,显示内存正在被回收,这是一件好事。你知道线程在多长时间后被清理吗?我显示有6个废弃的连接清理线程,加上附加监听器显示了18分钟并且还在增加。
让我们在聊天中继续这个讨论。
经过16小时的运行时间,我可以看到总加载的类为8,118,比昨天显著增加,而总卸载的类为68。堆现在的平均值在350MB到700MB之间,而PermGen增加了17MB。如果我点击一个生成PDF的函数,总加载数现在为8,536,而总卸载数仍然为68。如果我关闭PDF窗口,总加载和卸载的类仍然保持在较高的水平,执行完毕后类不应该卸载吗?
当类加载器变得不可达时,类将被卸载。但只有当相关的类加载器变得不可达时才会发生这种情况。如果这是你的问题,你需要阅读有关类加载器内存泄漏的资料。