java - 为什么调整 java 堆分配大小会导致 OOME?

标签 java garbage-collection out-of-memory heap-memory heap-dump

为什么调整 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 的 javaopts 包括:

  • -Xmx3072m
  • -XX:+HeapDumpOnOutOfMemoryError
  • -XX:-UseGCOverheadLimit
  • -详细:gc
  • -Xss256k
  • -XX:MaxPermSize=256m
  • -服务器
  • -XX:+打印GCDetails

JVM 正在运行 1.6.0._24。我们现在不能更改版本,但在接下来的一两个月内会有一个窗口可以这样做。如果 1.6.0_45 稳定得多,我们将致力于切换到它。我们目前正在测试它。

机器只有 4GB 的总系统内存。此外,还有一个小型 RAM 磁盘也在使用中。我担心 Xmx 设置对于这个环境来说已经太高了。

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

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

最佳答案

为了阅读您的日志,您似乎有一个非常大的 Activity 爆发,最像大到足以直接进入终身/老一代的对象。我仍然建议您增加最大内存以查看您的应用程序的行为,因为 OOME 可能会给您带来困惑的统计信息。


这表明早期推广力度很大。 “GC”是一个次要集合,它似乎需要每个对象,触发 Full GC,它会找到一些可以丢弃的永久对象。当年轻的对象在 Eden 空间中消亡时,GC 工作得最好,但看起来您的大多数对象都在永久空间中消亡。

测试这个的一种方法是使最大堆空间更大。如果您可以尝试使用 24 GB 的堆或 80% 的主内存,看看它的表现如何。例如如果您有 32 GB 内存,请尝试 -Xmx24g。从这些数字来看,您似乎需要至少 5 GB 的 Eden 大小。

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

我会检查你有最新版本的 Java 6,比如更新 45。更新 18 和 26 之间有显着的性能改进。

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

相关文章:

java - JApplet Form 如何与 Servlet 通信?

java - 无法在 Spring Boot 中使用 java.util.Duration 作为 @Value 配置

java - 调用 `System.gc()` 是管理 Java 应用程序内存的好方法吗?

java - 在垃圾收集例程运行之前程序何时终止

c# - 一段时间后在 RabbitMQ 中获取 OutOfMemory

php - symfony 内存不足错误

java - 如何输出在Java中的void方法中更改的变量

java - 无法解析导入 org.dom4j.*

javascript - JavaScript 中不需要的事件监听器的垃圾收集

Jenkins - java.lang.OutOfMemoryError : PermGen space -