背景
最近在公司推广 k8s 容器化。观察发现很多服务,每次部署完成后的readiness检查时间多很长,同时伴随着不少服务超时现象,做了一次简单的排查。
排查思路
每个jvm服务都使用skywalking做了链路追踪,该工具同时提供了jvm的监控。
观察启动时候的jvm监控如下:
可以看到,自启动后的短时间内存在大量的full gc。full gc对jvm进程的影响不言而喻,处理大量对象的标记过程,需要长时间的stop the world。
那为何会有如此多的full gc?观察发现堆内存并未出现明显瓶颈,使用率不到50%。观察 non-heap的监控,发现在full gc 的时间节点,正好是non-heap内存在逐渐增大的时间。所以怀疑non-heap内存和full gc 有关。
non-heap 具体是保留的什么信息?
java8中 non-heap 包括 metaspace 和compressed class space
什么是metaspace:java8 中对堆内存做了改动。java8之前“持久代”保存 类和方法的元数据以及运行时常量池等。java8中去掉了持久代, 类和方法的元数据放到了non-heap中的metaspace中,运行时常量池等放到了堆中。
什么是compressed class space:指针压缩的,提高性能。
metaspace默认空间通常是20M,可以通过 java -XX:+PrintFlagsInitial 查看下
java -XX:+PrintFlagsInitial | grep Metaspace
uintx InitialBootClassLoaderMetaspaceSize = 4194304 {product}
uintx MaxMetaspaceExpansion = 5452592 {product}
uintx MaxMetaspaceFreeRatio = 70 {product}
uintx MaxMetaspaceSize = 18446744073709551615 {product}
uintx MetaspaceSize = 21810376 {pd product}
uintx MinMetaspaceExpansion = 340784 {product}
uintx MinMetaspaceFreeRatio = 40 {product}
bool UseLargePagesInMetaspace = false {product}
或者jinfo
jinfo -flag MetaspaceSize 1
-XX:MetaspaceSize=21807104
从监控中可以看到non-heap在启动后呈快速上涨的态势,也就是说在不断的扩容。而metaspace的扩容会触发full gc(与jdk7里的perm空间不足类似),进而会导致大量fullgc的产生。
那具体会使用多少metaspace,可以通过jstat 命令查看
jstat -gc 1 1s
S0C S1C S0U S1U EC EU OC OU MC MU CCSC CCSU YGC YGCT FGC FGCT GCT
69888.0 69888.0 2500.7 0.0 559232.0 425977.0 1398144.0 144682.3 140416.0 133154.9 17536.0 16275.6 1092 17.329 4 0.817 18.146
69888.0 69888.0 2500.7 0.0 559232.0 435761.5 1398144.0 144682.3 140416.0 133154.9 17536.0 16275.6 1092 17.329 4 0.817 18.146
69888.0 69888.0 2500.7 0.0 559232.0 441501.2 1398144.0 144682.3 140416.0 133154.9 17536.0 16275.6 1092 17.329 4 0.817 18.146
69888.0 69888.0 2500.7 0.0 559232.0 448339.7 1398144.0 144682.3 140416.0 133154.9 17536.0 16275.6 1092 17.329 4 0.817 18.146
关注这么几个指标:MC:方法区大小 MU:方法区使用大小 CCSC:压缩类空间大小 CCSU:压缩类空间使用大小。
可见MU 有140多M,20M初始值是远不够的,所以会触发扩容,引起full gc。
解决思路
做些jvm参数调优即可,指定metaspaceSize
-XX:MetaspaceSize=256m -XX:MaxMetaspaceSize=256m
这里的256M并非指定占多少空间,而是metaspace触发full gc的阈值大小。