FullGC排查,居然是它!

前言

我是[提前退休的java猿](https://juejin.cn/user/465848660928872 "https://juejin.cn/user/465848660928872"),一名7年java开发经验的开发组长,分享工作中的各种问题!(抖音、公众号同号)

🔈 今天这篇文章一定值得你看 转载前公司CTO发的文章:FullGC排查,居然是它

项目上有 Full GC 告警,每个节点平均每天有一到两次Full GC 告警。出现Full GC时,应用是处于 Stop The World 状态,对于高可用要求的应用来说,这是一个很大的稳定性风险。就在告警出现的这几天,上游业务方也联系到我,说他们近期调用我们的接口超时,通过监控来看,调用超时的时间正好也就是Full GC出现的时间。虽然我其实并没有排查过Full GC问题,不过我还是想试一下。

问题分析排查

通过查看Full GC日志,发现Full GC是由于达到Metaspace Threadhold 触发,GC耗时在2-4S。

js 复制代码
2025-09-27T14:33:02.210+0800: 312596.342:  
[FullGC(MetadataGCThreshold) 3169M->553M(6144M), 3.5247678secs]  
[Eden: 1258.0M(3594.0M)->0.0B(3686.0M) Survivors: 22528.0K->0.0B Heap: 3169.3M(6144.0M)->554.0M(6144.0M)], [Metaspace: 752742K->303689K(1511424K)]  
[Times: user=4.20 sys=0.43, real=3.52 secs]

既然是元空间,那我们通过应用监控查看当时的元空间情况,当Metaspace 达到700多MB时,就会触发Full GC, GC后元空间降到300多MB,释放了400M内存。

元空间主要是存放 class 信息,看一下类数量的监控,在FullGC时,类数量达到15万,GC后降低到4万,释放了11万个class。

正常情况下 class 是相对稳定的,通常是由于动态代理、序列化、脚本引擎这类场景会动态生成类。

通过类监控的增长曲线发现,工作时间的类增长曲线明显比非工作时间的类增长曲线更陡峭,说明大概率是工作时间调用的某个接口的内部逻辑导致。

通过接口调用数量监控,判断可能和其中三个接口有关。于是从接口调用入口出发捋代码逻辑,找到一段调用 Aviator 框架的代码。

java 复制代码
AviatorEvaluatorInstance instance = ExpressContext.getInstance();  
ExpressionLexer lexer = new ExpressionLexer(instance, expression);  
CodeGenerator codeGenerator = new OptimizeCodeGenerator(instance, null, CLASS_LOADER, null);
ExpressionParser parser = new ExpressionParser(instance, lexer, codeGenerator);
parser.parse();

这段代码看着就可疑,又是CodeGenerator,又用到ClassLoader,这些类名仿佛就是在和我说,别找了,就是我。

于是写了一段单元测试代码,循环调用,jvm启动参数上加上

ruby 复制代码
-XX:+TraceClassLoading -XX:+TraceClassUnloading

运行后发现有大量类的生成和卸载,完全满足触发 Metapsace Threshold 的条件。此时此刻,我已经有80%的把握就是它的问题了。不过熟悉我的人都知道,我不仅是一个解决问题能力还不错的人,同时我还是一个保守的人, 没有十足的把握我是不会下结论,我可不想在新团队砸了自己招牌。

现在一切猜想都很符合逻辑,现在就差最后的证明,那就是类统计信息。在这不像我以前的时候,各种环境我都是超管权限,想怎么弄就怎么弄。我无法登录到容器内部用jcmd或者 jmap histo命令去统计类,容器平台上提供了堆dump的功能,但是堆dump是一个非常重量级的操作,为了不影响业务,我只能等。

在国庆期间,大家都休假了,我做了一个决定,我偷偷的登录到云平台,关闭其中一个节点的流量,导出堆内存,导入MAT进行分析。此时我心情是平静的,我知道我对了,我只是要让MAT告诉我。我本将心向明月,然明月何曾是两乡,MAT没有辜负我,它小声的和我说:你对了。

解决方案

这些类就是Aviator框架生成的,类名后缀是顺序递增,总共生成过100多万个class。解决方案通过增加表达式的LRU缓存解决这个问题,上线后类数量非常平稳,Full GC也消失了。加LRU缓存也只是临时方案,最好是Aviator官方能支持我这种场景,给他们提了issue,不过大概率他们是不会同意的,毕竟它也只是一个个人项目,靠爱发电不太靠得住。你看我,想啥时候发就啥时候发。

github.com/killme2008/...

相关推荐
我命由我1234528 分钟前
Android Studio - Android Studio 自定义预览尺寸
android·java·ide·java-ee·android studio·android-studio·android runtime
mCell1 小时前
你以为短链接只是 Hash + 301/302?
后端·算法·架构
咖啡八杯1 小时前
GoF设计模式——迭代器模式
java·后端·设计模式·迭代器模式
AIGS0012 小时前
企业AI落地的关键认知:向量空间JBoltAI的本体语义平台
java·人工智能·人工智能ai大模型应用
KaMeidebaby3 小时前
卡梅德生物技术快报|抗体亲和力成熟工业化调控新机制:差异性浆细胞增殖工艺优化思路
java·开发语言·人工智能·算法·机器学习·架构·spark
醉城夜风~4 小时前
Java详解经典算法题:接雨水(三种实现方案+原理剖析)
java·开发语言·算法
数智化转型推荐官5 小时前
2026统一身份管理系统五大发展趋势解读
java·运维·微服务
看菜鸡互5 小时前
探索用 SlideML 让大模型生成 PPT 的实验方法
java·运维
IT_陈寒5 小时前
SpringBoot自动配置不是你以为的那样的智能
前端·人工智能·后端
程序员张37 小时前
SpringBoot集成BCrypt密码加密库
java·spring boot·后端