乐趣区

聊聊jvm的CompressedClassSpace


本文主要研究一下 jvm 的 CompressedClassSpace
CompressedClassSpace

java8 移除了 permanent generation,然后 class metadata 存储在 native memory 中,其大小默认是不受限的,可以通过 -XX:MaxMetaspaceSize 来限制
如果开启了 -XX:+UseCompressedOops 及 -XX:+UseCompressedClassesPointers(默认是开启),则 UseCompressedOops 会使用 32-bit 的 offset 来代表 java object 的引用,而 UseCompressedClassPointers 则使用 32-bit 的 offset 来代表 64-bit 进程中的 class pointer;可以使用 CompressedClassSpaceSize 来设置这块的空间大小
如果开启了指针压缩,则 CompressedClassSpace 分配在 MaxMetaspaceSize 里头,即 MaxMetaspaceSize=Compressed Class Space Size + Metaspace area (excluding the Compressed Class Space) Size

查看 CompressedClassSpace 大小
jcmd pid GC.heap_info
/ # jcmd 1 GC.heap_info
1:
Shenandoah Heap
524288K total, 144896K committed, 77232K used
2048 x 256K regions
Status: not cancelled
Reserved region:
– [0x00000000e0000000, 0x0000000100000000)

Metaspace used 45675K, capacity 46867K, committed 47104K, reserved 1091584K
class space used 5406K, capacity 5838K, committed 5888K, reserved 1048576K
可以看到整个 metaspace 使用了 45675K,其中 class space 使用了 5406K,而 Metaspace area (excluding the Compressed Class Space)使用了 45675K-5406K=40269K;整个 metaspace 的 reserved 大小为 1091584K,其中 class space 的 reserved 大小为 1048576K
jcmd pid VM.native_memory
/ # jcmd 1 VM.native_memory
1:

Native Memory Tracking:

Total: reserved=2224403KB, committed=238187KB
– Java Heap (reserved=524288KB, committed=144896KB)
(mmap: reserved=524288KB, committed=144896KB)

– Class (reserved=1092940KB, committed=48460KB)
(classes #8563)
(instance classes #7988, array classes #575)
(malloc=1356KB #20589)
(mmap: reserved=1091584KB, committed=47104KB)
(Metadata:)
(reserved=43008KB, committed=41216KB)
(used=40286KB)
(free=930KB)
(waste=0KB =0.00%)
(Class space:)
(reserved=1048576KB, committed=5888KB)
(used=5407KB)
(free=481KB)
(waste=0KB =0.00%)

– Thread (reserved=37130KB, committed=2846KB)
(thread #36)
(stack: reserved=36961KB, committed=2676KB)
(malloc=127KB #189)
(arena=42KB #70)

– Code (reserved=529360KB, committed=15420KB)
(malloc=968KB #4745)
(mmap: reserved=528392KB, committed=14452KB)

– GC (reserved=21844KB, committed=7724KB)
(malloc=5460KB #9644)
(mmap: reserved=16384KB, committed=2264KB)

– Compiler (reserved=165KB, committed=165KB)
(malloc=34KB #455)
(arena=131KB #5)

– Internal (reserved=3758KB, committed=3758KB)
(malloc=1710KB #6582)
(mmap: reserved=2048KB, committed=2048KB)

– Other (reserved=32KB, committed=32KB)
(malloc=32KB #3)

– Symbol (reserved=10277KB, committed=10277KB)
(malloc=7456KB #225421)
(arena=2821KB #1)

– Native Memory Tracking (reserved=4235KB, committed=4235KB)
(malloc=10KB #126)
(tracking overhead=4225KB)

– Arena Chunk (reserved=176KB, committed=176KB)
(malloc=176KB)

– Logging (reserved=7KB, committed=7KB)
(malloc=7KB #264)

– Arguments (reserved=18KB, committed=18KB)
(malloc=18KB #500)

– Module (reserved=165KB, committed=165KB)
(malloc=165KB #1708)

– Safepoint (reserved=4KB, committed=4KB)
(mmap: reserved=4KB, committed=4KB)

– Unknown (reserved=4KB, committed=4KB)
(mmap: reserved=4KB, committed=4KB)
可以看到 class 部分,reserved 大小为 1092940KB,其中 Metadata 的 reserved 大小为 43008KB,Class space 的 reserved 大小为 1048576KB;其中 Metadata 使用了 40286KB,而 Class space 使用了 5407KB
jmx 查看
@GetMapping(“/meta”)
public Object getMetaspaceSize(){
return ManagementFactory.getPlatformMXBeans(MemoryPoolMXBean.class)
.stream()
.filter(e -> MemoryType.NON_HEAP == e.getType())
.filter(e -> e.getName().equals(“Metaspace”) || e.getName().equals(“Compressed Class Space”))
.map(e -> “name:”+e.getName()+”,info:”+e.getUsage())
.collect(Collectors.toList());
}
输出如下:
/ # curl -i localhost:8080/memory/meta
HTTP/1.1 200
Content-Type: application/json;charset=UTF-8
Transfer-Encoding: chunked
Date: Sun, 31 Mar 2019 03:06:55 GMT

[“name:Metaspace,info:init = 0(0K) used = 46236784(45153K) committed = 47710208(46592K) max = -1(-1K)”,”name:Compressed Class Space,info:init = 0(0K) used = 5482736(5354K) committed = 6029312(5888K) max = 1073741824(1048576K)”]
这里可以看到 Metaspace 总共使用了 45153K,其中 Compressed Class Space 部分使用了 5354K,而 Metaspace area (excluding the Compressed Class Space)使用了 45153K-5354K=39799K;而这里显示的 Metaspace 的 max 为 -1,其中 Compressed Class Space 部分 max 值为 1048576K 即 1G
spring boot 应用查看
/ # curl -i “http://localhost:8080/actuator/metrics/jvm.memory.used?tag=area:nonheap”
HTTP/1.1 200
Content-Disposition: inline;filename=f.txt
Content-Type: application/vnd.spring-boot.actuator.v2+json;charset=UTF-8
Transfer-Encoding: chunked
Date: Sun, 31 Mar 2019 02:52:51 GMT

{“name”:”jvm.memory.used”,”description”:”The amount of used memory”,”baseUnit”:”bytes”,”measurements”:[{“statistic”:”VALUE”,”value”:6.4449464E7}],”availableTags”:[{“tag”:”id”,”values”:[“CodeHeap ‘non-profiled nmethods'”,”CodeHeap ‘profiled nmethods'”,”Compressed Class Space”,”Metaspace”,”CodeHeap ‘non-nmethods'”]}]}

/ # curl -i “http://localhost:8080/actuator/metrics/jvm.memory.used?tag=area:nonheap&tag=id:Metaspace”
HTTP/1.1 200
Content-Disposition: inline;filename=f.txt
Content-Type: application/vnd.spring-boot.actuator.v2+json;charset=UTF-8
Transfer-Encoding: chunked
Date: Sun, 31 Mar 2019 02:54:56 GMT

{“name”:”jvm.memory.used”,”description”:”The amount of used memory”,”baseUnit”:”bytes”,”measurements”:[{“statistic”:”VALUE”,”value”:4.7468312E7}],”availableTags”:[]}

/ # curl -i “http://localhost:8080/actuator/metrics/jvm.memory.used?tag=area:nonheap&tag=id:Compressed%
20Class%20Space”
HTTP/1.1 200
Content-Disposition: inline;filename=f.txt
Content-Type: application/vnd.spring-boot.actuator.v2+json;charset=UTF-8
Transfer-Encoding: chunked
Date: Sun, 31 Mar 2019 02:55:18 GMT

{“name”:”jvm.memory.used”,”description”:”The amount of used memory”,”baseUnit”:”bytes”,”measurements”:[{“statistic”:”VALUE”,”value”:5609952.0}],”availableTags”:[]}
springboot 使用 micrometer,通过 /actuator/metrics 接口提供相关指标查询功能,其中 Metaspace 及 Compressed Class Space 在 jvm.memory.used 这个 metric 中它是基于 MemoryPoolMXBean 来实现的,具体详见 micrometer-core-1.1.3-sources.jar!/io/micrometer/core/instrument/binder/jvm/JvmMemoryMetrics.java
小结

java8 移除了 permanent generation,然后 class metadata 存储在 native memory 中,其大小默认是不受限的,可以通过 -XX:MaxMetaspaceSize 来限制;如果开启了 -XX:+UseCompressedOops 及 -XX:+UseCompressedClassesPointers(默认是开启),则 UseCompressedOops 会使用 32-bit 的 offset 来代表 java object 的引用,而 UseCompressedClassPointers 则使用 32-bit 的 offset 来代表 64-bit 进程中的 class pointer;可以使用 CompressedClassSpaceSize 来设置这块的空间大小
开启了指针压缩,则 CompressedClassSpace 分配在 MaxMetaspaceSize 里头,即 MaxMetaspaceSize=Compressed Class Space Size + Metaspace area (excluding the Compressed Class Space) Size

查看 CompressedClassSpace 的内存使用情况有好几种方法:

jcmd pid GC.heap_info(Metaspace 为总的部分,包含了 class space,而 Metaspace area (excluding the Compressed Class Space)需要自己计算即 total-class space)
jcmd pid VM.native_memory(class 为总的部分,包含了 Metaspace area (excluding the Compressed Class Space)及 Class Space)
使用 JMX 来获取 NON_HEAP 类型中的 name 为 Metaspace 及 Compressed Class Space 的 MemoryPoolMXBean 可以得到 Metaspace 及 Compressed Class Space 的使用情况 (JMX 得到的 Metaspace 为总的部分,而 Metaspace area (excluding the Compressed Class Space) 需要自己计算即 total-class space)
如果是 springboot 应用,它使用 micrometer,通过 /actuator/metrics 接口提供相关指标查询功能,其中 Metaspace 及 Compressed Class Space 在 jvm.memory.used 这个 metric 中

doc

Class Metadata
7.19.3 -XX:CompressedClassSpaceSize
JDK 8: UseCompressedClassPointers vs. UseCompressedOops
JVM 源码分析之 Metaspace 解密
聊聊 jvm 的 PermGen 与 Metaspace

退出移动版