JVM超出了使用-Xmx定义的最大内存限制。

18

我们有一个Java网络应用程序,我们将其从Java 1.5.0.19升级到Java 1.6.0.21

/usr/java/jdk1.6.0_21/bin/java -server -Xms2000m -Xmx3000m -XX:MaxPermSize=256m -Djava.awt.headless=true -Dwg.environment=production -Djava.io.tmpdir=/var/cache/jetty -Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.port=31377 -Dcom.sun.management.jmxremote.authenticate=true -Dcom.sun.management.jmxremote.ssl=false -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/tmp/webapp -Dprogram.name=run.sh -Djava.endorsed.dirs=/opt/3p/jboss/lib/endorsed -classpath /opt/3p/jboss/bin/run.jar:/usr/java/jdk1.6.0_21/lib/tools.jar org.jboss.Main -c default

正如您所看到的,它应该预先分配2GB的堆,并且最大值为3GB(我们之所以预先分配这么多是因为这个应用程序很古老并且设计不良,因此有很多东西需要加载)。我们最近升级到1.6后遇到的问题是,偶尔会出现内存飙升的情况。虽然内存使用可能是一个应用程序问题,但JVM超过了设置的3GB最大堆限制。使用top命令我看到:

 PID USER      PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  COMMAND    
8449 apache    18   0 19.6g 6.9g 5648 S  4.0 84.8  80:42.27 java             

那么具有3GB堆、256MB permgen和一些开销的JVM如何消耗6.9GB?是JVM中的错误,可以通过升级到版本#35来修复吗?还是Java中缺少了使用额外内存的某些内容?只是想看看是否有人之前遇到过这个问题。


本地代码库不受堆或PermGen限制的约束(例如使用malloc分配的内存)。您是否使用任何重要的本地库? - Keith Randall
你使用的是哪个操作系统发行版? - Peter Ilfrich
不是的。我们使用了一堆Java库,但没有本机库。 - Benny the Guard
当内存像这样飙升时,实际的Java堆大小是多少? - jtahlborn
1
既然你在使用Linux,可以使用pmap命令来查找内存实际使用情况。 - parsifal
3个回答

15

那么,一个拥有3GB堆、256MB permgen及一些开销的JVM怎么会消耗6.9GB呢?

可能的解释包括:

  • 大量的线程堆栈,
  • 内存映射文件没有在需要关闭时被关闭,
  • 某些本地代码库使用(可能泄漏)堆外内存。

我倾向于先指责应用程序,而不是指责JVM。


1
这也是我的倾向,但我从未见过Java进程消耗太多。我们没有使用任何本地库,所以不确定是否会有问题。内存映射文件是否计入堆?线程堆栈我将尝试调查并查看有多少个。 - Benny the Guard
1
“内存映射文件不算在堆内存中吗?” 不算。映射的内存段是在Java堆之外分配的。 - Stephen C
好的。看起来线程堆栈是通过-Xss控制的,而我们没有明确设置。文档说这应该是1MB。我错了还是这意味着我们可以排除这个可能的问题?我们已经配置了JMX,所以我打算尝试一下看它是否有所帮助。 - Benny the Guard
只有在您知道没有大量线程时,才能排除线程堆栈问题。 (而生成大量线程是旧的设计不良的 Web 应用程序可能会做的事情。 ~4000 x 1Mb 是 ~4Gb ...) - Stephen C
只是查看该时间段的JMX统计数据。看到约65个线程,使用了约1GB堆,约100MB非堆,但操作系统内存非常高。因此不是堆,正在寻找内存映射文件或本地库的隐藏情况。 - Benny the Guard

12
长话短说,我的初步反应是正确的,JVM中存在一个bug。我们使用的是1.6.0_21版本,结果发现我们遇到了与https://confluence.atlassian.com/pages/viewpage.action?pageId=219023686中描述的完全相同的错误。将其升级至1.6.0_37后问题得以解决,我们从每日崩溃变成了两周没有崩溃。
因此,虽然不仅仅责怪JVM的情绪是一种好的策略,但似乎也应该告诫人们不要总是假设JVM没有bug,它就像所有软件一样偶尔也会出现漏洞。而且,保持更新似乎也是一个好的策略。
感谢您在这件事上提供的所有帮助!

1
是的。保持JVM安装程序更新的重要性。您使用了一个已经过时约2年(并且有7个带有安全补丁的版本!)的Java安装程序,因此遇到了问题。 - Stephen C

2

这在1.6升级之前并没有发生。但是,还有其他问题和其他应用程序更改。因此,我怀疑确实存在应用程序错误,我们将尝试查看刚刚获得的堆转储。虽然我理解可能会有一些额外的开销,但超过3 GB的开销似乎过多了。线程、无头等真的会使用那么多吗? - Benny the Guard
这取决于线程,特别是它们的调用堆栈。如果您在许多线程中使用了大量递归,这可能会导致大量内存使用(无法被垃圾回收清除)。 - Peter Ilfrich

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接