我在Tomcat 7上运行的Java Web应用程序似乎有内存泄漏。应用程序的平均内存使用量在负载下随着时间线性增加(使用JConsole确定)。在内存使用率达到稳定水平后,性能会显着下降。响应时间从~100ms到[300ms,2500ms],所以这实际上是造成实际问题。
我的应用程序的JConsole内存配置文件:
使用VisualVM,我看到至少有一半的内存被字符数组(即char [])使用,并且字符串的大多数(大致相同数量的每个,300,000个实例)是以下之一:分配失败“,”复制“,”次要GC的结束“,所有这些似乎都与垃圾收集通知有关。据我所知,应用程序根本不监视垃圾收集器。 VisualVM找不到任何这些字符串的GC根,所以我很难跟踪它。
内存分析器堆转储:
我无法解释为什么内存使用情况会如此,但我有一个关于为什么性能会降低的理论。如果内存碎片化,应用程序可能需要很长时间才能分配连续的内存块来处理新请求。
将此内容与内置的Tomcat服务器状态应用程序进行比较后,内存会增加并保持平稳状态,但不会像我的应用程序那样达到较高的“楼层”。它也没有大量无法访问的char []。
Tomcat服务器状态应用程序的JConsole内存配置文件:
Tomcat服务器状态applicationp的内存分析器堆转储:
这些字符串可以在哪里分配,为什么它们不被垃圾收集?是否存在可能影响此问题的Tomcat或Java设置?是否有特定的包可能会影响这个?
答案 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"
我无法再获得详细的内存堆转储,但内存配置文件看起来要好得多:
24小时后,内存配置文件看起来一样:
答案 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>
内存分析器
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 中,您可以尝试:
至于工具: JavaMelody 可能对持续统计感兴趣,但需要付出努力。
答案 7 :(得分:0)