【问题标题】:Java HeapDumps indicate that used Heap Size is 30% smaller than actual heap definition after OutOfMemory exceptionsJava HeapDumps 表明在 OutOfMemory 异常后使用的堆大小比实际堆定义小 30%
【发布时间】:2016-10-06 05:34:10
【问题描述】:

在 JVM 抛出 OutOfMemory 异常后,我正在分析一些堆转储。我在 Windows 2008R2 平台上使用 Hotspot JDK 1.7(64 位)。应用服务器是一个 JBoss 4.2.1GA,通过Tanuki Java Service Wrapper 启动。

它使用以下参数启动:

wrapper.java.additional.2=-XX:MaxPermSize=256m
wrapper.java.initmemory=1498
wrapper.java.maxmemory=3000
wrapper.java.additional.19=-XX:+HeapDumpOnOutOfMemoryError

翻译成:

-Xms1498m -Xmx3000m -XX:MaxPermSize=256m -XX:+HeapDumpOnOutOfMemoryError

还有一些其他的 GC & JMX 配置参数。

我的问题是,当我使用 Eclipse Memory Analyzer 分析由 OutOfMemoryException 创建的堆转储时,MAT 总是显示 2.3G 或 2.4G 的堆大小。我已经在 MAT 中启用了Keep Unreachable Objects 的选项,所以我不相信 MAT 正在修剪堆。

java.lang.RuntimeException: java.lang.OutOfMemoryError: GC overhead limit exceeded

java.lang.OutOfMemoryError: Java heap space

MAT 中的摘要:

Size: 2.3 GB Classes: 21.7k Objects: 47.6m Class Loader: 5.2k

我的实际堆文件大小约为 3300KB,因此符合我的 3000m 最大堆大小设置。

那么 MAT 中缺少的 500-600M 内存在哪里?为什么 MAT 只显示我的堆大小为 2.4G?

SO 上的其他帖子倾向于表明它是 JVM 在转储堆之前进行了一些 GC,但是如果缺少 500M 是由于 GC,为什么它甚至首先抛出 OOM?如果 GC 真的可以清理 500M(或者我的堆的近 25%),那么 JVM 真的内存不足吗?

有没有办法调整堆转储,以便获得堆的完整/完整图片(包括丢失的 500M)?

如果不是,我发现我真的很难找到我首先遇到这些 OOM 的方式/原因。

应某人的要求,我附上了来自活动节点的jstat -gc <PID> 1000 的输出:http://pastebin.com/07KMG1tr

【问题讨论】:

  • MAT 只打印一个存储在堆转储文件头中的数字。这个数字并没有说明转储文件中对象图的完整性。

标签: java garbage-collection jvm heap-dump eclipse-memory-analyzer


【解决方案1】:

您使用的是哪个 GC? 您可能缺少 Eden,请尝试使用 jstat - Java Virtual Machine Statistics Monitoring Tool

【讨论】:

  • 据我所知,它使用的是默认 GC,据我所知,它是 ParallelGC。
  • 你能分享一下你的 jstat 输出吗?
  • 我应该为jstat 使用您希望看到的任何特定选项/配置吗?
  • jstat -gc PID 1000 应该够了
  • 显然访问生产 JVM 以运行 jstat 并不像我想象的那么简单,并且需要几个不同级别的干预。统计数据有多重要?
【解决方案2】:

java.lang.OutOfMemoryError: 超出 GC 开销限制

这并不一定意味着您的堆已满,请参阅this Q&A

java.lang.OutOfMemoryError: Java 堆空间

这并不意味着你的堆还剩下 0 个字节,这意味着分配请求无法得到满足。如果有东西试图分配 600MB 并且只剩下 500MB,那么这将引发 OOME。

如果不是,我发现我真的很难找到我遇到这些 OOM 的方式/原因。

获取堆栈跟踪以查看执行相关分配的调用站点是否有任何可疑之处将是一个开始。或者您可以尝试增加堆大小,看看问题是否消失。

【讨论】:

  • 我意识到GC overhead limit exceeded 并不意味着它已满,而是它无法回收足够的空间。然而,如果它不need 来回收额外的空间,它会抛出这种类型的异常,这似乎很奇怪。而且我无法想象在 java 中有什么方法可以在一次调用中分配 500MB 的内存块。也许初始分配给具有大量大小的数组或哈希表/等,但除此之外,我无法想象任何会分配该大小的内存块的东西。我错过了什么明显的东西吗?
猜你喜欢
  • 1970-01-01
  • 2013-01-09
  • 2011-07-18
  • 2012-04-02
  • 2013-03-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-09-17
相关资源
最近更新 更多