jdk11-zgc-gc时间不断增长

1.现象

为了降低gc时间,我们打算对一批服务安装jdk11,使用zgc。在对zgc进行测试期间,发现随着程序的运行,gc时间越来越长。如下图所示:

每分钟gc总耗时

同时进程的gc次数并没有发生太大变化,如下图所示:

每分钟gc总次数

zgc的 gc.time 只和 GC Roots 相关,关于zgc可以参考:https://tech.meituan.com/2020/08/06/new-zgc-practice-in-meituan.html

既然gc次数没有变多,所以应该是GC Roots 数量增长

2.原因分析

我们去查看近几天的gc日志,会发现 Subphase: Pause Roots ClassLoaderDataGraph  这个阶段的耗时会不断增加

gc日志

这时候开始怀疑GC Roots有问题

此时查看metaspace元空间监控,发现metaspace不断增长,metaspace里的对象是被认为GC Roots。所以因为metaspace不断增长,导致GC Roots越来越多,最终导致gc时间越来越长

元空间内存占用情况

为什么metasapce空间不断增长呢?

metaspace空间主要保存对象类信息,我们查看关于class相关监控信息

加载class信息监控

可以看到我们程序不断load class,但是没有unload class,所以导致meta内存一直增长

原因大概也就清楚了,但是此时产生疑问,为什么程序原先使用jdk8 cms gc算法,没有这个问题呢。

我们查看一下之前使用cms算法相关监控


cms机器监控信息

可以看到这台 cms 机器,也不断load class,但是他也会 unload class,所以metaspace一直维持平稳

因为我们在使用cms时,设置了一个jvm参数:CMSClassUnloadingEnabled 。该参数表示在进行cms,进行class卸载

3. zgc

为什么zgc不会收 meta:使用的jdk11不支持回收,jdk12才支持

在JDK11,zgc垃圾回收器目前还只是实验性的功能,只支持Linux/x64平台。后续优化接改进,短时间内无法更新到JDK11中,所以可能会遇到一些不稳定因素。例如: 1. JDK12支持并发类卸载功能。2. JDK13将可回收内存从4TB支持到16TB。3. JDK14提升稳定性的同时,提高性能。4. JDK15从实验特性转变为可生产特性 。所以对于一些大量使用反射、动态代理、CGLIB和Javasist等频繁自定义类加载器的场景中,ZGC难以处理Metaspace的巨大内存压力。

4.为什么metaspace一直升高

我们对运行的进程进行class统计

jcmd {pid} GC.class_stats

加载的class类信息

发现存在大量的jdk.internal.reflect.GeneratedConstructorAccessor class类

我们对上述的内容进行前缀统计,命令如下:

 jcmd {pid} GC.class_stats |awk '{print$13}'|sed  's/\(.*\)\.\(.*\)/\1/g'|sort |uniq -c|sort -nrk1

class前缀统计

发现 jdk.internal.reflect 包名下的类文件,占用了很大一部分,并且随着程序运行,jdk.internal.reflect这个类文件越来越多。

这个就是我们在使用jdk反射时,会自动生成的类字节码,被jvm加载进metaspace。随着程序运行,加载类字节码越来越多,但是没有释放,导致meta越来越大。

5.解决办法

-Dsun.reflect.inflationThreshold 可以控制通过反射生成字节码。(该值表示 反射调用多少次 才开始生成字节码)

当把该参数这是int 最大值时,说明永不生成字节码。

从stackoverflows摘抄一段话

When using Java reflection, the JVM has two methods of accessing the information on the class being reflected. It can use a JNI accessor, or a Java bytecode accessor. If it uses a Java bytecode accessor, then it needs to have its own Java class and classloader (sun/reflect/GeneratedMethodAccessor class and sun/reflect/DelegatingClassLoader). Theses classes and classloaders use native memory. The accessor bytecode can also get JIT compiled, which will increase the native memory use even more. If Java reflection is used frequently, this can add up to a significant amount of native memory use. The JVM will use the JNI accessor first, then after some number of accesses on the same class, will change to use the Java bytecode accessor. This is called inflation, when the JVM changes from the JNI accessor to the bytecode accessor. Fortunately, we can control this with a Java property. The sun.reflect.inflationThreshold property tells the JVM what number of times to use the JNI accessor. If it is set to 0, then the JNI accessors are always used. Since the bytecode accessors use more native memory than the JNI ones, if we are seeing a lot of Java reflection, we will want to use the JNI accessors. To do this, we just need to set the inflationThreshold property to zero.

If you are on a Oracle JVM then you would only need to set:  -Dsun.reflect.inflationThreshold=2147483647

If you are on IBM JVM, then you would need to set:   -Dsun.reflect.inflationThreshold=0

大概意思说:使用java反射,内部实现有2种方式,一种是jni,另一种是生成字节码方式。生成字节码方式会占用更多内存,但是性能会好一点。sun.reflect.inflationThreshold 代表,反射执行多少次后,开始使用字节码方式,当我们把这个参数设置为int最大值,代表永不使用字节码方式,也就没有内存问题了。

具体原理参考:

https://www.moregeek.xyz/i/880000804235

https://stackoverflow.com/questions/16130292/java-lang-outofmemoryerror-permgen-space-java-reflection

重新加上 -Dsun.reflect.inflationThreshold=2147483647 这个参数,后来gc.time 次数就一直稳定下来了,并且metaspace空间也不增长了

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,607评论 6 507
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,239评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,960评论 0 355
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,750评论 1 294
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,764评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,604评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,347评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,253评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,702评论 1 315
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,893评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,015评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,734评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,352评论 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,934评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,052评论 1 270
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,216评论 3 371
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,969评论 2 355

推荐阅读更多精彩内容

  • 1、从编码到执行 解释执行和编译执行是可以混合的,执行次数多的代码,会进行 JIT 的编译,交由操作系统直接执行。...
    ArthurHC阅读 333评论 0 2
  • Catalog 1 怎么解决OOM?/ 怎么排查OOM?/ JVM调优1.1 JDK自带工具1.2 阿里开源JVM...
    allen锅阅读 363评论 0 1
  • 解决服务器进程退出问题(metaspace溢出) 现象策划反应服务器进不去,远程看了一下进程消失了(crash)有...
    landon30阅读 3,369评论 0 2
  • 运行时数据区域 jdk 1.8之前与之后的内存模型有差异,方法区有变化(https://cloud.tencent...
    陈晨_软件五千言阅读 1,226评论 2 12
  • 本文所有内容来于:http://stuq.com/a/100ww java代码是如何执行的 java代码是运行于j...
    良辰美景TT阅读 2,748评论 0 89