Java web应用程序中的内存泄漏

Java web应用程序中的内存泄漏,java,tomcat,web,Java,Tomcat,Web,我有一个在Tomcat7上运行的Java web应用程序,它似乎存在内存泄漏。应用程序在加载时的平均内存使用量随时间线性增加(使用JConsole确定)。内存使用达到稳定期后,性能会显著下降。响应时间从~100ms到[300ms,2500ms],因此这实际上造成了真正的问题 我的应用程序的JConsole内存配置文件: 使用VisualVM,我发现至少有一半的内存被字符数组(即char[])使用,并且字符串中的大多数(每个字符串的数量大致相同,300000个实例)是以下字符串之一:“分配失败”

我有一个在Tomcat7上运行的Java web应用程序,它似乎存在内存泄漏。应用程序在加载时的平均内存使用量随时间线性增加(使用JConsole确定)。内存使用达到稳定期后,性能会显著下降。响应时间从~100ms到[300ms,2500ms],因此这实际上造成了真正的问题

我的应用程序的JConsole内存配置文件:

使用VisualVM,我发现至少有一半的内存被字符数组(即char[])使用,并且字符串中的大多数(每个字符串的数量大致相同,300000个实例)是以下字符串之一:“分配失败”、“复制”、“次要GC结束”,所有这些似乎都与垃圾收集通知有关。据我所知,应用程序根本不监视垃圾收集器。VisualVM找不到这些字符串的GC根,所以我很难找到它

内存分析器堆转储:

我无法解释为什么内存使用会如此稳定,但我有一个理论,解释为什么一旦出现这种情况,性能就会下降。如果内存是碎片化的,应用程序可能需要很长时间才能分配连续的内存块来处理新请求

与内置的Tomcat服务器状态应用程序相比,内存会增加,并在某个级别趋于稳定,但不会像我的应用程序那样达到很高的“地板”。它也没有太多无法访问的字符[]

Tomcat服务器状态应用程序的JConsole内存配置文件:

Tomcat服务器状态应用程序的内存分析器堆转储p:


这些字符串可以分配到哪里?为什么不进行垃圾收集?是否有Tomcat或Java设置会影响这一点?是否有特定的软件包会影响这一点?

我建议使用MemoryanAnalyzer分析堆,它提供了更多的信息。
有一个独立的应用程序和eclipse嵌入式应用程序。
您只需要在应用程序上运行jmap,并用它分析结果。

由于这听起来不具体,JSF可能是一个候选者。但我本来也希望散列映射也会泄漏

如果您使用JSF: 在web.xml中,您可以尝试:

  • javax.faces.STATE_保存_方法客户端
  • com.sun.faces.numberofviewsin会话0
  • com.sun.faces.numberofLogicalView1

至于工具:JavaMelody可能对持续的统计数据很感兴趣,但需要努力。

我可以推荐每个Java安装附带的jvisualvm。启动程序,连接到Web应用程序。转到监视器->堆转储。现在可能需要一些时间(取决于大小)。 通过堆转储进行导航非常简单,但是您必须自己弄清楚它的含义(虽然不太复杂),例如

转到(在heapdump中),选择java.lang.String,右键单击在实例视图中显示。之后,您将在左侧表中看到系统中当前活动的字符串实例。 单击一个字符串实例,您将在右表的右上部分看到一些字符串首选项,如字符串的值


在右表的右下部分,您将看到这个字符串实例是从引用的。在这里,您必须检查大多数*字符串*从何处引用。但是对于您的情况(176/210,很可能很快就会找到一些导致问题的字符串示例),在检查问题所在之后,应该会很清楚。

平台是由可用内存下降到默认百分比阈值以下导致的,这会导致完全GC。这解释了为什么JVM在试图查找和释放内存时不断暂停,导致性能下降

我通常建议查看对象缓存,但在您的情况下,我认为您的堆大小对于Tomcat实例+webapp来说太小了。我建议将堆增加到1G(
-Xms1024m-Xmx1024m
),然后再次检查内存使用情况

如果您仍然看到相同的行为,那么您应该进行另一个堆转储,并查看字符串和字符之后最大的使用者。根据我的经验,这通常是缓存机制。如果可能,可以进一步增加内存或减少缓存存储。有些缓存只定义对象的数量,所以您需要了解每个缓存对象的大小

一旦您了解了内存使用情况,就可以再次降低内存,但IMHO 512MB将是最低要求

更新:


您不必担心无法访问的对象,因为它们应该由GC清理。另外,按类型划分,最大的使用者是字符串和字符,这是很正常的。大多数对象都包含某种字符串,因此按频率划分,字符串和字符是最常见的。理解包含字符串的对象的内容是找到内存使用者的关键。

我只是在一个完全不同的应用程序中遇到了相同的问题,所以tomcat7可能不是问题的根源。Memory Analyzer在进程中显示了1000万个无法访问的字符串实例(已运行了大约2个月),其中大多数/所有实例都具有与垃圾收集相关的值(例如,“分配失败”、“小GC结束”)

内存分析器

完整GC现在每2秒运行一次,但不会收集这些字符串。我猜我们在GC代码中遇到了一个bug。我们使用以下java版本:

$ java -version
java version "1.7.0_06"
Java(TM) SE Runtime Environment (build 1.7.0_06-b24)
Java HotSpot(TM) 64-Bit Server VM (build 23.2-b09, mixed mode)
以及以下VM参数:

-Xms256m -Xmx768m -server -XX:+DisableExplicitGC -XX:+UseConcMarkSweepGC 
-XX:+UseParNewGC -XX:+CMSParallelRemarkEnabled -XX:NewSize=32m -XX:MaxNewSize=64m
-XX:SurvivorRatio=8 -verbose:gc -XX:+PrintGCTimeStamps -XX:+PrintGCDetails 
-Xloggc:/path/to/file

我从
tomcat\bin\setenv.bat
中删除了以下JMX配置:

set "JAVA_OPTS=%JAVA_OPTS% 
    -Dcom.sun.management.jmxremote=true 
    -Dcom.sun.management.jmxremote.port=9090
    -Dcom.sun.management.jmxremote.ssl=false
    -Dcom.sun.management.jmxremote.authenticate=false"
我无法获得详细的内存堆
tomcat.util.buf.StringCache.byte.enabled=true
#tomcat.util.buf.StringCache.char.enabled=true
#tomcat.util.buf.StringCache.trainThreshold=500000
#tomcat.util.buf.StringCache.cacheSize=5000