本文 的 原文 地址
原始的内容,请参考 本文 的 原文 地址
尼恩说在前面
在40岁老架构师 尼恩的读者交流群(50+)中,最近有小伙伴拿到了一线互联网企业如得物、阿里、滴滴、极兔、有赞、希音、百度、网易、美团的面试资格,遇到很多很重要的面试题:
- 频繁 fullgc,如何排查?
最近有小伙伴在面试 希音,又遇到了相关的面试题。
小伙伴 没系统梳理, 支支吾吾的说了几句,面试官不满意, 挂了。
其中第一道题目的答案是:
希音面试:ClickHouse Group By 执行流程 ?CK 能支持 十亿级数据 实时分析的原理 是什么?
其中第2道题目的答案是:
希音面试:es延时如何解决?在mysql+ canal同步 es建索引场景,这个延时如何解决?
这篇文章,帮助小伙伴回答 第3题。
所以,尼恩给大家做一下系统化、体系化的梳理,使得大家内力猛增,可以充分展示一下大家雄厚的 “技术肌肉”,让面试官爱到 “不能自已、口水直流”,然后实现”offer直提”。
当然,这道面试题,以及参考答案, 会收入咱们的 《尼恩Java面试宝典PDF》V175版本,供后面的小伙伴参考 ,帮助大家进大厂/做架构。
架构师视角的 FullGC 治理思维
频繁 FullGC 不仅仅是 “JVM 问题”,而是 “架构不合理” 的外在表现。
作为技术高手,需要高维度看问题,具备以下高维度思考 能力:
(1) 系统化思维:
不孤立看待 FullGC,而是关联 “代码→JVM→架构→业务”,从 “现象→瓶颈→根因” 分层排查;
(2) 预防大于治疗:
通过架构设计(如缓存分片、流处理)避免内存资源过载,而非依赖事后调优;
(3) 数据驱动决策
所有优化方案需基于监控数据(如 Heap Dump、接口延迟)验证效果,避免 “凭经验调参”。
频繁 FullGC 的治理目标: “建立一套可扩展的内存资源管理体系”,确保业务增长时,系统能通过架构升级(而非临时调优)应对内存压力,从根本上保障服务稳定性。
作为技术高手, 需跳出 “仅调优 JVM 参数” 的局限,从底层原理→外部观测→ 四步定位→ e2e 解决方案 形成闭环。
根因分析包括: 底层原理→外部观测→ 四步定位
**e2e 解决方案:**从 “紧急止血” 到 “架构优化” , 核心路径 包括 “紧急止血(1-3小时) →局部代码优化 与 JVM 优化(1-3天) → 架构升级 (1-3个月)” 三个大步骤,确保问题彻底解决。

温馨提示: 尼恩的方案, 帮助很多小伙伴 进 大厂,逆天改命。
这个方案也是一个能逆天改命的方案,大家 收藏起来 看他10遍20遍,毒打面试官。

一、底层原理:理解 FullGC 的触发机制与危害
在解决问题前,必须先明确 FullGC 的核心逻辑 :
FullGC 是 JVM 老年代(Old Gen)或元空间(Metaspace)内存不足时,由垃圾收集器(如 G1、CMS、Serial Old)执行的 “全量垃圾回收”。
FullGC 特点是STW(Stop-The-World)时间长、资源消耗高,频繁触发会直接导致系统吞吐量下降、响应延迟飙升,甚至引发服务雪崩。
1.1 核心触发条件(底层逻辑)
FullGC 并非 “随机发生”,而是 JVM 内存管理机制的必然结果,关键触发条件可归纳为以下 4 类:
| 触发场景 | 底层原因 | 典型案例 |
|---|---|---|
| 老年代内存不足 | 新生代对象晋升老年代(如大对象直接进入老年代、Survivor 区对象年龄达标),老年代剩余空间无法容纳 | 批量处理 100MB 以上的大文件、缓存未及时清理 |
| 元空间(Metaspace)溢出 | 类加载过多(如动态生成类、依赖包冲突导致类重复加载),元空间默认无上限(需手动配置) | Spring 动态代理生成大量代理类、Groovy 脚本频繁编译 |
| 显式调用 System.gc () | 代码中手动触发 FullGC(JVM 可能忽略,但部分场景会强制执行) | 错误的 “内存优化” 代码、第三方框架隐式调用 |
| GC 算法特殊逻辑 | 如 CMS 收集器的 “Concurrent Mode Failure”(并发回收时老年代满)、G1 的 “Humongous Allocation Failure”(大对象无法分配) | 高并发下大对象突发写入、G1 Region 划分不合理 |
1.2 频繁 FullGC 的危害(技术高手 视角)
频繁 FullGC 是 “系统 可能雪崩” 的核心信号,其危害远超 “性能慢”:
危害1:服务可用性骤降:
每次 FullGC 会导致 STW(即使 G1 可控制在百毫秒级,频繁触发仍会累积延迟),秒杀、支付等核心场景会出现 “请求超时”;
所有GC算法在Full GC阶段都会发生Stop-The-World (STW),即所有业务线程被挂起,全力进行垃圾回收。
对于高并发、低延迟的核心场景(如秒杀、支付、交易),即使G1/ZGC已将STW控制在百毫秒级,频繁触发(如每分钟数次)也会导致请求响应时间尖刺,大量用户请求超时,直接触发熔断,服务可用性骤降。
危害2:资源恶性循环:
FullGC 消耗 CPU / 内存资源,导致业务线程执行时间变长,对象创建速度加快,进一步加剧内存压力,形成 “FullGC 越频繁→系统越慢→内存越紧张” 的死循环;
“FullGC 越频繁→系统越慢→内存越紧张” 的死循环 如下:
-
Full GC消耗CPU:GC线程是CPU密集型任务,频繁Full GC会抢占业务线程的CPU时间片。
-
业务线程效率降低:业务线程获得CPU时间减少,执行变慢,请求堆积。
-
对象堆积加速:未能及时处理的请求会导致新对象无法释放,在堆中加速堆积。
-
内存压力剧增:对象堆积使得下一次Full GC更快到来且耗时更长。
危害3:死亡螺旋(Death Spiral)导致雪崩:
单节点频繁 FullGC 可能扩散为集群问题(如分布式缓存穿透导致各节点频繁创建对象),若不及时止血,系统 可能雪崩 。
系统陷入 “FullGC 越频繁 → 系统越慢 → 内存越紧张 → FullGC更频繁” 的死亡螺旋(Death Spiral),最终资源耗尽,进程僵死或崩溃。
二、FullGC 常见根因 分析
先看 一个真实的FullGC 生产案例 :
商品中心 QPS 3w → 每 30min 一次 FGC → 连续 5s 停顿

根因:
缓存未命中时DB查询 返回全字段,平均对象 2MB,每秒 3000 次 → 6GB/min 进入 Old 区
核心方案是 字段裁剪:
DB查询可以 返回 DTO 仅含前端所需 7 个字段,体积降到 80k(-96%)
FullGC 常见根因
| 根因 | 现象特征 | 架构级解法 |
|---|---|---|
| 1. 本地缓存超配 | Old 区 80% 被 CHM 本地缓存占满 | 本地缓存改用具备自动淘汰能力的 Caffeine + 外部化 Redis;bigkey分片 |
| 2. 消息膨胀 | Kafka 大消息 >512k,Old 区瞬涨 | 消息瘦身(传 ID 不传体);压缩 snappy;分块传输 |
| 3. 查询放大 | 1 次 DB查询 返回 10MB List | 分页 + 游标 + 字段裁剪 |
| 4. 滥用本地变量导致内存泄漏 | ThreadLocal 未 remove,Old 区缓慢上涨 | 可观测线程池;TransmittableThreadLocal |
| 5. 反射滥用/ASM 滥用 | LambdaMetafactory 产生大量类加载,MetaSpace 触发 FGC | 缓存 MethodHandle; |
| 6、其他 |
FullGC 常见根因 分类
| 根因分类 | 典型特征 | 排查方法 | 解决方案 |
|---|---|---|---|
| 内存泄漏 | 老年代使用率 只增不减, 直至OOM | jstat、MAT分析 查看支配树与GC Roots |
1. 修复代码bug(如无效引用) 2. 优化缓存策略(TTL、弱引用) 3. 检查框架资源未关闭(连接池等) |
| 代码BUG | 短命大对象 直接进入老年代 | jstat、GC日志 关注晋升年龄 |
1. 避免在循环中创建大对象 2. 优化集合的使用(如clear()) 3. 调整-XX:MaxTenuringThreshold |
| 缓存类应用 | 老年代被 缓存数据填满 | jstat、MAT分析 |
1. 使用堆外缓存(如Ehcache off-heap) 2. 使用分布式缓存(Redis) 3. 限制本地缓存大小(Guava Cache) |
| GC参数不当 | 堆空间配置 不合理 | 分析GC日志 | 1. 调整新生代与老年代比例(-XX:NewRatio) 2. 调整Eden与Survivo |
频繁FullGC排查全攻略

最低0.47元/天 解锁文章
5448

被折叠的 条评论
为什么被折叠?



