【问题标题】:eclipse memory analyzer sees small part (363,2MB) of entire heap dump (8GB)eclipse 内存分析器看到整个堆转储(8GB)的一小部分(363,2MB)
【发布时间】:2013-06-09 17:57:32
【问题描述】:

我试图调查java.lang.OutOfMemoryError: GC limit exceeded,它发生在我们部署在 tomcat 中的 web 应用程序的高负载时。堆大小设置为 8GB (-Xms2048m -Xmx8192m)

在某个时间点,由于 GC 活动开销,我们的应用程序变得无响应。我可以在日志中看到 Full GC 连续发生多次。所以我使用以下命令(jmap -F -dump:format=b,file=/root/dump2.hprof 4963)进行了堆转储。包含转储的文件大小为 9GB。进行转储后(应用程序被冻结了大约 45 分钟),发生了多次完整的 GC,直到抛出 OutOfMemoryError

这是一个 GC 活动的日志示例

[Full GC [PSYoungGen: 932096K->875513K(1864128K)] [ParOldGen: 5592447K->5592447K(5592448K)] 6524543K->6467961K(7456576K) [PSPermGen: 112285K->112285K(262144K)], 12.3954040 secs] [Times: user=47.60 sys=0.43, real=12.39 secs]
[Full GC [PSYoungGen: 932096K->890562K(1864128K)] [ParOldGen: 5592447K->5592447K(5592448K)] 6524543K->6483009K(7456576K) [PSPermGen: 112285K->112285K(262144K)], 12.6131900 secs] [Times: user=48.45 sys=0.49, real=12.61 secs]
[Full GC [PSYoungGen: 932096K->895268K(1864128K)] [ParOldGen: 5592447K->5592447K(5592448K)] 6524543K->6487715K(7456576K) [PSPermGen: 112285K->112285K(262144K)], 12.9488670 secs] [Times: user=49.61 sys=0.46, real=12.95 secs]

Heap
 PSYoungGen      total 1864128K, used 896698K [0x0000000755560000, 0x0000000800000000, 0x0000000800000000)
  eden space 932096K, 96% used [0x0000000755560000,0x000000078c10e8a8,0x000000078e3a0000)
  from space 932032K, 0% used [0x000000078e3a0000,0x000000078e3a0000,0x00000007c71d0000)
  to   space 932032K, 0% used [0x00000007c71d0000,0x00000007c71d0000,0x0000000800000000)
ParOldGen       total 5592448K, used 5592447K [0x0000000600000000, 0x0000000755560000, 0x0000000755560000)
  object space 5592448K, 99% used [0x0000000600000000,0x000000075555ff30,0x0000000755560000)
PSPermGen       total 262144K, used 112285K [0x00000005e0000000, 0x00000005f0000000, 0x0000000600000000)
  object space 262144K, 42% used [0x00000005e0000000,0x00000005e6da7530,0x00000005f0000000)

heap dump is taken (ca 45minutes freeze)
[Full GC [PSYoungGen: 932096K->903362K(1864128K)] [ParOldGen: 5592447K->5592447K(5592448K)] 6524543K->6495810K(7456576K) [PSPermGen: 112285K->112285K(262144K)], 2883.9864390 secs] [Times: user=49.41 sys=0.47, real=2884.17 secs]
[Full GC [PSYoungGen: 932096K->897728K(1864128K)] [ParOldGen: 5592447K->5592444K(5592448K)] 6524543K->6490173K(7456576K) [PSPermGen: 112288K->112288K(262144K)], 13.3092680 secs] [Times: user=50.75 sys=0.40, real=13.31 secs]

为了分析堆转储,我在 Eclipse 内存分析器 (MAT) 中打开了它。不幸的是,MAT 显示堆大小为 363.2MB(在概览选项卡或堆转储详细信息选项卡中),而根据 GC 日志,堆已填满 6467961K (6.4G)。 Unreachable Objects Histogram 总共显示 75 511 736 (75 MB)。直方图视图也证实了总浅堆为 380 837 136 (363.2MB)

我的问题是,如果 GC 无法回收内存,为什么 MAT 不显示堆转储中的所有对象?

env details:
Eclipse Memory Analyzer Version 1.2.1
heap dump taken on
java version "1.7.0_13"
Java(TM) SE Runtime Environment (build 1.7.0_13-b20)
Java HotSpot(TM) 64-Bit Server VM (build 23.7-b01, mixed mode)

以下是 MAT 中导入堆转储的截图:

【问题讨论】:

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


    【解决方案1】:

    MAT 默认不显示无法访问的对象。

    您可以通过转到 Preferences -> Memory Analyzer -> Keep Unreachable Objects 来启用该选项。启用该选项后再次加载堆。

    启用该选项后,它将显示完整的堆。 即使我处于同样的情况,也无法在网上获得太多信息,我的经理向我展示了这个选项。希望它有所帮助。

    【讨论】:

    • 好建议。这也帮助我找到了 Apache PoI 中的错误
    【解决方案2】:

    默认情况下,MAT 会从其视图中隐藏所有 无法访问的对象,因为这些对象已被标记为垃圾回收。

    可以在以下视图中访问无法访问的对象:

    1. 关闭快照
    2. 使用 Window > Heap Dump History 右键单击​​选择堆转储,删除索引文件
    3. 使用“窗口”>“首选项”>“内存分析器”选择“保留无法访问的对象”
    4. 重新打开堆转储,这将重新解析堆转储。
    5. 选择 Java 基础 > GC Roots 查询。
    6. 选择“无法访问的对象”行。
    7. 对该行运行“显示保留集”查询。

    这将显示所有对象的直方图,通常是 无法访问,并且将在下一次机会时被垃圾收集。作为 这些对象现在在快照中,然后可以在其中进行检查 更详细。

    有关详细信息,请参阅: Eclipse MAT Reference - Unreachable objects

    【讨论】:

      【解决方案3】:

      这是 MAT 在收集如此大堆的堆转储时的常见行为。我经常收集 8GB 堆的堆转储,通常会得到一个 MAT 配置文件,其中显示约 1GB 的活动对象。

      45 分钟的冻结也是预期的。我的解释是在堆转储收集期间会发生几个FullGC 周期,这会减少实际获得的堆转储的大小。但我还没有找到官方解释或参考记录为什么会有这么大的差异。

      进一步,请参阅此参考资料 - MAT Does Not Show the Complete Heap

      症状:在交互式监控内存使用情况时,使用的堆大小比 MAT 报告的要大得多。

      在创建索引期间,内存分析器会删除无法访问的对象,因为各种垃圾收集器算法往往会留下一些垃圾(如果对象太小,移动和重新分配地址会很昂贵)。但是,这应该不超过 3% 到 4%。如果您想知道删除了哪些对象,请按照此处的说明启用调试输出:MemoryAnalyzer/FAQ#Enable_Debug_Output

      【讨论】:

        【解决方案4】:

        实际上,当您对java.lang.OutOfMemoryError: GC limit exceeded 问题进行故障排除时,我不建议您使用 jmap 实用程序。当 JVM 进行垃圾处理(主要集合数量过多)时,我发现这种方法存在问题。

        请改用以下方法,看看您是否从 MAT 解析和分析过程中获得了更好的结果:

        • 添加以下JVM HotSpot参数-XX:+HeapDumpOnOutOfMemoryError
        • 再次复制问题并等待 OOM 事件。堆转储的生成速度应该更快,这将增加数据的价值和有效性。
        • JVM 将在 OOM 事件之后生成 JVM Heap Dump(HPROF 格式)。
        • 在 MAT 中再次加载 JVM 堆转储,看看是否获得了更好的结果,例如更大的堆占用空间。

        问候, P-H

        【讨论】:

        • 我遇到了一种情况,我们使用上面的标志 -XX:+HeapDumpOnOutOfMemoryError 来生成堆转储,当我们遇到 java.lang.OutOfMemoryError: GC limit exceeded 时它没有生成堆转储问题继续触发 GC 调用以释放内存并将 CPU 使用率提高到非常高的水平并且服务器没有响应因此服务器在触发堆转储之前崩溃。当内存使用率超过 98% 时,必须使用 jmap 实用程序并获取堆转储,这并没有真正让我们在 OOM 时获取确切的快照
        【解决方案5】:

        我们最近在 MAT 中发现了一个错误,它只能看到堆的一个子集:https://bugs.eclipse.org/bugs/show_bug.cgi?id=404679#c3

        问题在于 JVM 写入了一个超过 4GB 的 HEAP_DUMP 记录,因此标头中的长度被包装了。

        您运行的是哪个版本的 MAT?请尝试更新的版本。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2011-08-28
          • 1970-01-01
          • 1970-01-01
          • 2011-02-28
          • 1970-01-01
          • 1970-01-01
          • 2015-11-09
          • 2013-01-21
          相关资源
          最近更新 更多