3

我正在研究在我的应用程序中调试“OutOfMemoryError: Metaspace”错误。在 OOME 之前,我在 gc 日志中看到以下内容:

{Heap before GC invocations=6104 (full 39):
 par new generation   total 943744K, used 0K [...)
  eden space 838912K,   0% used [...)
  from space 104832K,   0% used [...)
  to   space 104832K,   0% used [...)
 concurrent mark-sweep generation total 2097152K, used 624109K [...)
 Metaspace       used 352638K, capacity 487488K, committed 786432K, reserved 1775616K
  class space    used 36291K, capacity 40194K, committed 59988K, reserved 1048576K
2015-08-11T20:34:13.303+0000: 105892.129: [Full GC (Last ditch collection) 105892.129: [CMS: 624109K->623387K(2097152K), 3.4208207 secs] 624109K->623387K(3040896K), [Metaspace: 352638K->352638K(1775616K)], 3.4215100 secs] [Times: user=3.42 sys=0.00, real=3.42 secs] 
Heap after GC invocations=6105 (full 40):
 par new generation   total 943744K, used 0K [...)
  eden space 838912K,   0% used [...)
  from space 104832K,   0% used [...)
  to   space 104832K,   0% used [...)
 concurrent mark-sweep generation total 2097152K, used 623387K [...)
 Metaspace       used 352638K, capacity 487488K, committed 786432K, reserved 1775616K
  class space    used 36291K, capacity 40194K, committed 59988K, reserved 1048576K
}

据我所见,元空间容量甚至没有接近承诺的大小(在这种情况下,-XX:MaxMetaspaceSize=768m)。所以我怀疑元空间的碎片导致分配器无法为新的类加载器找到新的块。

我知道,-XX:PrintFLSStatistics但这仅涵盖 CMS,而不是本机内存。

所以我的问题是:有没有类似于PrintFLSStatisticsHotspot 原生内存的调试帮助?

这是对 linux-amd64 JRE (1.8.0_45-b14) 使用 Java HotSpot(TM) 64 位服务器 VM (25.45-b02)。

4

2 回答 2

4

我刚刚研究了 HotSpot 中元空间的实现。元空间被分成块并使用空闲列表进行管理。所以碎片确实是你的问题的一个可能原因。

我还查看了 HotSpot VM ( -XX:+UnlockDiagnosticVMOptions -XX:+PrintFlagsFinal) 的标志,发布版本中没有标志。

但是,dump()Metaspace 类中有一个方法似乎是通过设置-XX:+TraceMetadataChunkAllocation标志来触发的。还有一个-XX:+TraceMetavirtualspaceAllocation听起来你感兴趣的。但是,这些是“开发”标志,这意味着您需要 VM 的调试版本。

于 2015-08-20T12:12:43.670 回答
2

@loonytune 的回答很好,但我想提供更多细节:

对于上下文,“元空间”是元空间的集合,每个类加载器一个。每个元空间都包含一个VirtualSpace对象列表,其中Metachunk分配了不同大小的 s。这些块MetaBlock包含 s,它们是元数据的真正容器。

我需要一个调试 JRE 来运行这些标志,所以按照这个教程我检查了 openjdk 存储库(我将检出重命名为,vm因为构建脚本似乎对jdk8文件夹名称有问题),运行

~/vm$ bash configure --enable-debug
~/vm$ DISABLE_HOTSPOT_OS_VERSION_CHECK=ok make all

并将结果vm/build/linux-x86_64-normal-server-fastdebug/images/j2re-image用作我的 java 运行时。

生成的日志行如下所示:

VirtualSpaceNode::take_from_committed() 不可用 8192 字空间 @ 0x00007fee4cdb9350 128K, 94% 已使用 [0x00007fedf5e22000, 0x00007fedf5f13000, 0x00007fedf5f22000, 0x00007fedf6022000)

这表明当前VirtualSpace已满,无法容纳请求8192 word大小的另一块。这将导致此元空间切换到另一个VirtualSpace.

ChunkManager::chunk_freelist_allocate: 0x00007fee4c0c39f8 块 0x00007fee15397400 大小 128 计数 0 空闲块总 7680 计数 15

ChunkManager::chunk_freelist_allocate: 0x00007fee4c0c39f8 块 0x00007fedf6021000 大小 512 计数 14 空闲块总 7168 计数 14

Metachunk分配一个新的时会发生这种情况,在第一种情况下,它有 128 个字大并且用完小块的列表。如您所见,下一个请求转到中等大小的块(大小为 512),总共留下 14 个空闲块。一旦空闲总数达到 0,就需要 Full GC 来增加总 Metaspace 大小。

请注意,指定-verbose会使您从上述两个标志中获得更多输出。

于 2015-08-20T18:06:28.690 回答