这几天生产上有台机器的Metaspace一直在告警,Metaspace使用达到了97%。使用 -XX:MetaspaceSize=512m ,告警也还在在持续,查看MC只有81536.0,显然这个参数没起作用。
也有人遇到类似的问题,并在openjdk上提过类似的bug,其实是一个注释的bug,最终在 JDK-8151845 中修复了。
查看了Oracle的手册,Metaspace的GC会在committed size达到high-water mark之后发生。并且GC之后high-water mark会变化:变大或者变小,变大的话会防止下次GC发生得太早。high-water mark的默认初始大小20.8M,通过 MetaspaceSize 来设置,可见MetaspaceSize是控制Metaspace发生GC的阈值。GC后high-water mark的变化,通过MaxMetaspaceFreeRatio和MinMetaspaceFreeRatio控制。
"类元数据在卸载相应的Java类时被解除分配。由于垃圾回收,Java类被卸载,并且可以诱导垃圾回收来卸载类并解除分配类元数据。当为类元数据提交的空间达到某个级别 (高水位线) 时,将诱导垃圾收集。在垃圾回收之后,根据从类元数据中释放的空间量,可以提高或降低高水位线。高水位线将被提高,以免过早引发另一次垃圾收集。高水位标记最初设置为命令行选项-XX:MetaspaceSize的值。[...]-XX:MetaspaceSize的默认大小取决于平台,范围从12 MB到大约20 MB。”
MaxMetaspaceSize默认为-1,无限大。不过如果没有限制的话,一直增大会被系统干掉进程。最好还是设置一下,比如1G。
下面是我测试了分别设置MetaspaceSize、MaxMetaspaceSize、InitialBootClassLoaderMetaspaceSize为1G,Metaspace的变化。
-XX:MetaspaceSize=1024m
-XX:MaxMetaspaceSize=1024m
-XX:InitialBootClassLoaderMetaspaceSize=1024m
三个参数都是没有改变init的大小,但是InitialBootClassLoaderMetaspaceSize改变了committed的大小,其实也是最终我们要的设置。
关于这个参数,可以看你假笨的关于 Metaspace的源码解读 ,发现的有点晚了。
最后的解决方案是使用这个配置: -XX:MaxMetaspaceSize=1024m -XX:InitialBootClassLoaderMetaspaceSize=256m 。
-XX:MaxNewSize=512m JVM堆区域新生代内存的最大可分配大小(PermSize不属于堆区), 生产环境建议设为800M-1024M-XX:MetaspaceSize 表示的并非是元空间的大小,它的含义是:主要控制matesaceGC发生的初始阈值,也就是最小阈值。也就是说当使用的matespace空间到达了MetaspaceSize的时候,就会触发Metaspace的GC。
-XX:MetaspaceSize和-XX:MaxMetaspaceSize设置元空间初始大小以及最大可分配大小。
例子:设置初始大小是100M,最大可分配空间也是100M。-XX:MetaspaceSize=100m -XX:MaxMetaspaceSize=100m。
2.如果元空间内存不够用,就会报OOM。
3.默认情况下,对应一个64位的服务端JVM来说,其默认的-XX:MetaspaceSize值为21MB,这就是初始的高水位线,一旦元空间的大小触及这个高水位线,就会触发Full GC并会卸载没有用的类,然后高水位线的值将会被重置。
4.从第3点可以知道,如果初始化的高水位线设置过低,会频繁的触发Full GC,高水位线会被多次调整。所以为了避免频繁GC以及调整高水位线,建议将-XX:MetaspaceSize设置为较高的值,而-XX:MaxMetaspaceSize不进行设置。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)