我正在研究在我的应用程序中调试“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
}
从我所看到的,Metaspace容量甚至没有接近承诺的大小(在这种情况下,-XX:MaxMetaspaceSize=768m
)。所以我怀疑Metaspace的碎片导致分配器无法为新的类加载器找到新的块。
我知道-XX:PrintFLSStatistics
但只涵盖CMS,而不是本机内存。
所以我的问题是:是否有类似于PrintFLSStatistics
的调试帮助可用于Hotspot的本机内存?
这是针对linux-amd64 JRE(1.8.0_45-b14)使用Java HotSpot(TM)64位服务器VM(25.45-b02)。
答案 0 :(得分:3)
我刚刚研究了HotSpot中Metaspace的实现。 Metaspace分为块并使用freelist进行管理。所以碎片确实是你问题的一个可能原因。
我还查看了HotSpot VM的标志(-XX:+UnlockDiagnosticVMOptions -XX:+PrintFlagsFinal
),发布版本中没有标记。
但是,Metaspace类中有一个dump()
方法,似乎是通过设置-XX:+TraceMetadataChunkAllocation
标志触发的。还有-XX:+TraceMetavirtualspaceAllocation
听起来对你很感兴趣。但是,这些是“开发”标志,这意味着您需要VM的调试版本。
答案 1 :(得分:1)
@ loonytune的答案很好,但我想提供更多细节:
对于上下文,“Metaspace”是元空间的集合,每个类加载器一个。每个元空间都包含VirtualSpace
个对象的列表,其中分配了Metachunk
个不同大小的对象。这些块保存MetaBlock
个,这是元数据的真正容器。
我需要一个调试JRE来运行这些标志,所以在this tuorial之后我检查了openjdk存储库(我将结帐重命名为vm
,因为构建脚本似乎对{{1}有问题文件夹名称),运行
jdk8
并使用生成的~/vm$ bash configure --enable-debug
~/vm$ DISABLE_HOTSPOT_OS_VERSION_CHECK=ok make all
作为我的java运行时。
生成的日志行如下所示:
VirtualSpaceNode :: take_from_committed()不可用8192字空间@ 0x00007fee4cdb9350 128K,94%使用[0x00007fedf5e22000,0x00007fedf5f13000,0x00007fedf5f22000,0x00007fedf6022000)
表示当前vm/build/linux-x86_64-normal-server-fastdebug/images/j2re-image
已满,无法保留请求的VirtualSpace
大小的另一个块。这将导致此元空间切换到另一个8192 word
。
ChunkManager :: chunk_freelist_allocate:0x00007fee4c0c39f8 chunk 0x00007fee15397400 size 128 count 0 Free chunk total 7680 count 15
ChunkManager :: chunk_freelist_allocate:0x00007fee4c0c39f8 chunk 0x00007fedf6021000 size 512 count 14 Free chunk total 7168 count 14
当分配新的VirtualSpace
时会发生这种情况,在第一种情况下,它是128字大并且用完了小块的列表。正如您所看到的,下一个请求将转到中等大小的块(大小为512)并总共释放14个块。一旦自由总数达到0,就需要使用Full GC来增加总Metaspace的大小。
请注意,指定Metachunk
可以从上述两个标志中获得更多输出。