2012-11-20 65 views
18

我有一个在Tomcat 7上运行的Java Web应用程序,它似乎有内存泄漏。当负载不足(使用JConsole确定)时,应用程序的平均内存使用量随时间线性增加。内存使用量达到高原后,性能显着下降。响应时间从〜100ms到[300ms,2500ms],所以这实际上导致了真正的问题。Java Web应用程序中的内存泄漏

我的申请的JConsole的存储器配置文件: application memory profile

使用的VisualVM,我看到至少有一半的存储器正在使用的字符阵列(即炭[])和大部分(大致相同数量的每个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设置?是否有特定的软件包可能会影响到这一点?

+0

你是如何确定这些字符串没有被特殊处理的? – djechlin

+0

当我的应用程序空闲时,我强制垃圾收集并使用VisualVM进行堆转储。这些字符串的数量总是随着时间的推移而增加,并且总是占已用内存的至少一半。 –

+0

内存分析器指出不可访问的char []和String对象占据了176MB的210MB堆。 –

回答

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

3

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

+0

Memory Analyzer确认VisualVM显示的内容;无法访问的char []和String对象占据了大约176MB的210MB堆。不幸的是,这并不能解释字符串来自哪里或者为什么他们没有被垃圾收集。 –

+0

我会尝试检查你的可达物体,而不是你无法达到的,我会猜想问题在那里。 – Zamir

0

由于这听起来没什么特别,所以一个候选人就是JSF。但是,我会希望散列图也会泄漏。

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

  • javax.faces.STATE_SAVING_METHOD 客户
  • com.sun.faces.numberOfViewsInSession
  • com.sun.faces。numberOfLogicalViews

至于工具:JavaMelody可能是持续的统计有趣,但需要努力。

2

我可以推荐jvisualvm与每一个Java安装走来。启动程序,连接到您的Web应用程序。转至监视器 - >堆转储。现在可能需要一些时间(取决于大小)。 通过Heap Dump的导航很容易,但是你必须弄明白自己的意思(尽管不是太复杂),例如,

转到(堆转储中),选择java.lang.String中,右击显示在实例查看。之后,您将在左侧表格中看到字符串当前在您的系统中处于活动状态的实例。 KLICK一个字符串例如,你会看到在右边表的右上部分一些字符串 preferenes,如字符串

在右表右下部分,你会看到这字符串实例从引用。在这里你必须检查你的* String *的大部分被引用的地方。但对于你的情况(176/210,很好的可以找到一些字符串很快就会引起你的问题的例子),在问题出现之后,应该清楚一些问题。

+0

我已经使用JVisualVM来获取堆转储,而内存分析器指出“泄漏”的字符串是无法访问的(即没有引用它们),我无法找到分配它们的内容。 –

2

高原是由于可用内存低于默认百分比阈值导致完全GC而引起的。这解释了为什么随着JVM在尝试查找和释放内存时不断暂停,性能下降。

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

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

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

更新:

您不必担心不可达的对象,因为他们应该被GC清理。此外,按类型排列的最大消费者是String和Char是很正常的 - 大多数对象都会包含某种类型的String,因此Strings和Chars是最常见的频率。了解持有包含字符串的对象的内容是查找内存使用者的关键。

2

我刚刚在一个完全不同的应用程序中遇到了同样的问题,所以tomcat7可能不是怪。 Memory Analyzer在进程中显示10M不可达字符串实例(已运行约2个月),其中大部分/全部都具有与垃圾收集相关的值(例如“分配失败”,“小型GC结束”)

内存分析器

Memory Analyzer

完全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 
+0

我在[内存分析器文档](http://wiki.eclipse.org/MemoryAnalyzer/FAQ)中发现了这个注释,表明它可能是“正常”的GC行为而不是bug: “在索引期间内存分析器会删除不可访问的对象,因为各种垃圾收集器算法往往会留下一些垃圾(如果对象太小,移动和重新分配地址会很昂贵)。但是,这应该不超过3 4%“。 –

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 
0

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

为此,请按照教程here

然后你可以运行一个简单的纪念品泄漏分析(This是一个很好的教程)

这应该很快就会导致你根本原因。