jvm metaspace泄露的问题往往不好排查。一个原因是我们不知道jvm加载1个类需要占用多少空间,以至于最后看到1个classloader加载了1000个类,他占用了10m空间是否是合理的。jvm提供了一种解决方式。利用jcmd查看metaspace。
查看空间占用
jcmd pid VM.metaspace
我们可以看到如下的信息。总共36个loaders。metaspace占用情况。
Total Usage ( 36 loaders):
Non-Class: 113 chunks, 7.09 MB capacity, 6.96 MB ( 98%) used, 129.19 KB ( 2%) free, 128 bytes ( <1%) waste, 7.06 KB ( <1%) overhead, deallocated: 61 blocks with 37.80 KB
Class: 51 chunks, 721.00 KB capacity, 695.72 KB ( 96%) used, 22.09 KB ( 3%) free, 8 bytes ( <1%) waste, 3.19 KB ( <1%) overhead, deallocated: 14 blocks with 5.09 KB
Both: 164 chunks, 7.80 MB capacity, 7.64 MB ( 98%) used, 151.27 KB ( 2%) free, 136 bytes ( <1%) waste, 10.25 KB ( <1%) overhead, deallocated: 75 blocks with 42.89 KB
Virtual space:
Non-class space: 8.00 MB reserved, 7.25 MB ( 91%) committed
Class space: 1.00 GB reserved, 768.00 KB ( <1%) committed
Both: 1.01 GB reserved, 8.00 MB ( <1%) committed
Chunk freelists:
Non-Class:
specialized chunks: 2, capacity 2.00 KB
small chunks: 14, capacity 56.00 KB
medium chunks: (none)
humongous chunks: (none)
Total: 16, capacity=58.00 KB
Class:
specialized chunks: 1, capacity 1.00 KB
small chunks: 7, capacity 14.00 KB
medium chunks: (none)
humongous chunks: (none)
Total: 8, capacity=15.00 KB
Waste (percentages refer to total committed size 8.00 MB):
Committed unused: 136.00 KB ( 2%)
Waste in chunks in use: 136 bytes ( <1%)
Free in chunks in use: 151.27 KB ( 2%)
Overhead in chunks in use: 10.25 KB ( <1%)
In free chunks: 73.00 KB ( <1%)
Deallocated from chunks in use: 42.89 KB ( <1%) (75 blocks)
-total-: 413.55 KB ( 5%)
MaxMetaspaceSize: 17179869184.00 GB
InitialBootClassLoaderMetaspaceSize: 4.00 MB
UseCompressedClassPointers: true
CompressedClassSpaceSize: 1.00 GB
查看classloader状态
因为是classloader相关,所以需要再进一步查看。例如我们可以查看loader的占用情况。
jcmd pid VM.classloader_stats
这里我们需要理解这36个classloader的情况,这里是根据loader分类的。其中有一个unsafe anonymous classes。这个是例如反射等动态产生的classloader,这种loader普遍的情况就是一个classloader加载一个class。这里加载了33个class,就寓意这里有33个classloader,加上剩下的3个,正好36个。
ClassLoader Parent CLD* Classes ChunkSz BlockSz Type
0x0000000000000000 0x0000000000000000 0x0000600002420000 1079 7921664 7906024 <boot class loader>
33 89088 58048 + unsafe anonymous classes
0x0000000800010a38 0x0000000800010eb0 0x00006000024240a0 5 86016 22048 jdk.internal.loader.ClassLoaders$AppClassLoader
0x0000000800010eb0 0x0000000000000000 0x0000600002424000 4 77824 22936 jdk.internal.loader.ClassLoaders$PlatformClassLoader
Total = 3 1121 8174592 8009056
ChunkSz: Total size of all allocated metaspace chunks
BlockSz: Total size of all allocated metaspace blocks (each chunk has several blocks)
在上面的信息中,我们可以看到classloader加载的Classes,ChunkSz,BlockSz我们从这里的信息中就可以知道哪些classloader占用比较大。
查看classloader委托情况
jcmd pid VM.classloaders
我们可以查看classloader的双亲委托情况,这样就可以拿到更多的运行时的信息,帮助解决问题
+-- <bootstrap>
|
+-- "platform", jdk.internal.loader.ClassLoaders$PlatformClassLoader
|
+-- "app", jdk.internal.loader.ClassLoaders$AppClassLoader
查看类的依赖情况
jcmd pid VM.class_hierarchy
最后一个是查看类的继承结构,这个是提供一种额外的信息,帮助我们找出问题点。