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

时间:2012-11-20 23:09:49

标签: java tomcat web

我在Tomcat 7上运行的Java Web应用程序似乎有内存泄漏。应用程序的平均内存使用量在负载下随着时间线性增加(使用JConsole确定)。在内存使用率达到稳定水平后,性能会显着下降。响应时间从~100ms到[300ms,2500ms],所以这实际上是造成实际问题。

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

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

内存分析器堆转储: heap dump, unreachable memory

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

将此内容与内置的Tomcat服务器状态应用程序进行比较后,内存会增加并保持平稳状态,但不会像我的应用程序那样达到较高的“楼层”。它也没有大量无法访问的char []。

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

Tomcat服务器状态applicationp的内存分析器堆转储: enter image description here

这些字符串可以在哪里分配,为什么它们不被垃圾收集?是否存在可能影响此问题的Tomcat或Java设置?是否有特定的包可能会影响这个?

8 个答案:

答案 0 :(得分:6)

我从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"

我无法再获得详细的内存堆转储,但内存配置文件看起来要好得多: enter image description here

24小时后,内存配置文件看起来一样: enter image description here

答案 1 :(得分:3)

我建议使用memoryAnalyzer来分析你的堆,它提供了更多的信息 http://www.eclipse.org/mat/ 有一个独立的应用程序和eclipse嵌入式应用程序。 你只需要在你的应用程序上运行jmap并用它来分析结果。

答案 2 :(得分:2)

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

转到(在heapdump中),选择 java.lang.String ,右键单击在实例视图中显示。之后,您将在系统中当前处于活动状态的左侧表 String 实例中看到。 点击一个 String 实例,你会在右表的右上角部分看到一些 String 优先,就像 String 的值

在右表的右下角部分,您会看到 字符串实例引用的位置。在这里,您必须检查引用大多数* String *的位置。但是根据你的情况(176/210,很好找到一些 String 的例子很快就会导致你的问题),在一些检查后应该清楚问题所在。

答案 3 :(得分:2)

平台是由可用内存降至默认百分比阈值以下导致Full GC引起的。这解释了为什么性能会随着JVM在尝试查找和释放内存时不断暂停而下降。

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

如果你仍然看到相同的行为,那么你应该采取另一个堆转储并查看String和Char之后的最大消费者。根据我的经验,这通常是缓存机制。如果可能的话,要么进一步增加内存,要么减少缓存存储。有些缓存只定义了对象的数量,因此您需要了解每个缓存对象的大小。

一旦你了解了你的内存使用情况,你可以再次降低它,但恕我直言512MB将是最低限度。

更新

您无需担心无法访问的对象,因为它们应该由GC清理。此外,按类型划分的最大消费者是String和Char是正常的 - 大多数对象都包含某种字符串,因此字符串和字符在频率上是最常见的。了解包含字符串的对象的内容是查找内存使用者的关键。

答案 4 :(得分:2)

我刚刚在一个完全不同的应用程序中遇到了同样的问题,所以tomcat7可能不应该受到指责。 Memory Analyzer在此过程中显示10M无法访问的String实例(已运行约2个月),并且大多数/所有实例都具有与垃圾收集相关的值(例如,“分配失败”,“次要GC结束”)< / p>

内存分析器

Memory Analyzer

Full GC现在每2秒运行一次,但这些字符串不会被收集。我的猜测是我们遇到了GC代码中的错误。我们使用以下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

答案 5 :(得分:1)

偶然地,我偶然发现了Tomcat的conf / catalina.properties文件中的以下几行,这些文件激活了字符串缓存。如果您打开了任何一个,这可能与您的情况有关。使用该功能似乎others are warning

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

答案 6 :(得分:0)

由于这听起来不明确,一个候选人应该是JSF。但后来我也希望哈希映射泄漏。

你应该使用JSF: 在 web.xml 中,您可以尝试:

  • javax.faces.STATE_SAVING_METHOD 客户
  • com.sun.faces.numberOfViewsInSession 0
  • com.sun.faces.numberOfLogicalViews 1

至于工具: JavaMelody 可能对持续统计感兴趣,但需要付出努力。

答案 7 :(得分:0)

尝试使用MAT并确保在解析heapdump时,不要删除无法访问的对象。

为此,请按照教程here

进行操作

然后你可以运行一个简单的Mem Leak Analysis(This是一个很好的教程)

这应该很快引导你找到根本原因。