为什么Java堆分配重新调整大小会导致OOME?

我们在日志中看到OutOfMemoryExceptions,它们似乎与Java堆提交大小从〜1G增长到〜2.4G一致。尽管出现错误消息,但似乎我们没有用完堆空间。除了引发异常(并生成堆转储)之外,重新调整大小似乎最终会成功,并且应用程序可以继续运行而不会出现问题(堆提交大小约为2.4G)。

这是日志输出的示例:

INFO   | jvm 1    | 2013/08/16 12:08:05 | [GC [PSYoungGen: 328000K->2997K(339200K)] 645686K->320683K(1038272K), 0.0101580 secs] [Times: user=0.01 sys=0.00, real=0.00 secs]
INFO   | jvm 1    | 2013/08/16 12:09:14 | [GC [PSYoungGen: 331509K->3487K(338816K)] 649195K->322153K(1037888K), 0.0115600 secs] [Times: user=0.02 sys=0.00, real=0.01 secs]
INFO   | jvm 1    | 2013/08/16 12:09:59 | [GC [PSYoungGen: 331999K->2928K(340032K)] 650665K->322608K(1039104K), 0.0099300 secs] [Times: user=0.02 sys=0.00, real=0.01 secs]
INFO   | jvm 1    | 2013/08/16 12:10:48 | [GC [PSYoungGen: 333104K->2723K(339648K)] 652784K->323240K(1038720K), 0.0100130 secs] [Times: user=0.02 sys=0.00, real=0.01 secs]
INFO   | jvm 1    | 2013/08/16 12:11:28 | [GC [PSYoungGen: 332885K->3884K(340864K)] 653402K->325089K(1039936K), 0.0106250 secs] [Times: user=0.02 sys=0.00, real=0.01 secs]
INFO   | jvm 1    | 2013/08/16 12:11:39 | [GC [PSYoungGen: 23694K->463K(340352K)] 344899K->323656K(2437504K), 0.0070330 secs] [Times: user=0.01 sys=0.00, real=0.00 secs]
INFO   | jvm 1    | 2013/08/16 12:11:39 | [GC [PSYoungGen: 463K->0K(340608K)] 323656K->323592K(2437760K), 0.0044440 secs] [Times: user=0.02 sys=0.00, real=0.01 secs]
INFO   | jvm 1    | 2013/08/16 12:11:39 | [Full GC
INFO   | jvm 1    | 2013/08/16 12:11:40 |  [PSYoungGen: 0K->0K(340608K)] [PSOldGen: 323592K->323592K(699072K)] 323592K->323592K(1039680K) [PSPermGen: 159297K->159297K(262144K)], 1.2076900 secs] [Times: user=1.20 sys=0.00, real=1.21 secs]
INFO   | jvm 1    | 2013/08/16 12:11:40 | [GC [PSYoungGen: 0K->0K(340736K)] 323592K->323592K(2437888K), 0.0046330 secs] [Times: user=0.02 sys=0.00, real=0.00 secs]
INFO   | jvm 1    | 2013/08/16 12:11:40 | [Full GC
INFO   | jvm 1    | 2013/08/16 12:11:42 |  [PSYoungGen: 0K->0K(340736K)] [PSOldGen: 323592K->279953K(744512K)] 323592K->279953K(1085248K) [PSPermGen: 159297K->159062K(262144K)], 1.7593100 secs] [Times: user=1.75 sys=0.00, real=1.76 secs]
INFO   | jvm 1    | 2013/08/16 12:11:42 | java.lang.OutOfMemoryError: Java heap space
INFO   | jvm 1    | 2013/08/16 12:11:42 | Dumping heap to java_pid28908.hprof ...
INFO   | jvm 1    | 2013/08/16 12:11:48 | Heap dump file created [463314899 bytes in 6.037 secs]
INFO   | jvm 1    | 2013/08/16 12:12:36 | [GC [PSYoungGen: 331840K->6044K(352192K)] 611793K->285998K(2449344K), 0.0164060 secs] [Times: user=0.02 sys=0.00, real=0.02 secs]
INFO   | jvm 1    | 2013/08/16 12:13:28 | [GC [PSYoungGen: 352156K->6161K(364160K)] 632110K->286114K(2461312K), 0.0152330 secs] [Times: user=0.02 sys=0.01, real=0.01 secs]
INFO   | jvm 1    | 2013/08/16 12:14:47 | [GC [PSYoungGen: 364113K->6575K(374144K)] 644066K->288169K(2471296K), 0.0179930 secs] [Times: user=0.02 sys=0.01, real=0.02 secs]


请注意,紧接OOME之前,已提交的总堆在1GB到2.4GB之间振荡。我们可以看到它之前稳定在1GB,之后稳定在2.4GB。

此1.6.0._24 JVM的javaopt中包括:


-Xmx3072m
-XX:+ HeapDumpOnOutOfMemoryError
-XX:-UseGCOverheadLimit
-verbose:gc
-Xss256k
-XX:MaxPermSize = 256m
-服务器
-XX:+ PrintGC详细信息


JVM正在运行1.6.0._24。我们现在无法更改版本,但将在下一个或两个月内提供一个窗口。如果1.6.0_45更稳定,我们将力争切换到该位置。我们目前正在测试。

该计算机仅具有4GB的系统总内存。另外,还有一个小的RAM磁盘在使用中。我担心Xmx设置对于这种环境已经太高了。

这让我们感到困惑,因为在发生异常时堆的使用量似乎并不大。我们为什么要得到这个OOME?

更新:我们正在尝试通过将初始内存(Xms)设置为等于最大内存(Xmx)来防止这种情况。到目前为止,尽管我们还没有介绍生产的变化,但是这些实验还是很有希望的。尽管似乎确实表明可以在不增加最大堆大小(或减少应用程序内存占用量)的情况下避免使用OOME,但仍然没有解释为什么OOME首先发生。那么,为什么堆大小调整会导致OOME仍然是个谜呢?

最佳答案

为了阅读日志,您似乎有大量的活动,最像是足够大的对象可以直接进入权属/老年代。我仍然建议您增加最大内存,以查看应用程序的行为,因为OOME可能会给您令人困惑的统计信息。



这表明大量的早期晋升。 “ GC”是一个次要集合,似乎每个对象都需要,从而触发一个完整GC,该GC查找可以删除的一些使用期限对象。当年轻物体在伊甸园空间中死亡时,GC效果最佳,但看来您的大多数物体都在保管空间中死亡。

一种测试方法是使最大堆空间更大。如果您可以尝试使用24 GB的堆或80%的主内存,请查看其行为。例如如果您有32 GB的内存,请尝试-Xmx24g。从这些数字看来,您想要的Eden大小至少为5 GB。

如果这不是一个选择,我建议您使用内存分析器将内存消耗减少至少三倍。

我将检查您是否有Java 6的最新版本,例如更新45。在更新18和26之间,性能有了显着提高。

关于java - 为什么Java堆分配重新调整大小会导致OOME?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/18255866/

10-10 03:26