FullGC 多久一次正常?


一则或许对你有用的小广告

欢迎加入小哈的星球,你将获得:专属的实战项目(4个项目都能学) / 1v1 提问 / 简历修改 / Java 学习路线 / 社群讨论 / 学习打卡 / 每月赠书

  • 《Spring AI 项目实战(问答机器人、RAG 智能客服、联网搜索)》已完结,基于 Spring AI + Spring Boot 3.x + JDK 21...查看介绍

  • 《从零手撸:仿小红书(微服务架构)》 已完结,基于 Spring Cloud Alibaba + Spring Boot 3.x + JDK 17...查看介绍;演示链接:http://116.62.199.48:7070/

  • 《从零手撸:前后端分离博客项目(全栈开发)》 2 期已完结,演示链接:http://116.62.199.48/

  • 新开坑项目:《从零手撸:秒杀系统高并发优化实战》 正在更新中...,查看介绍

截止目前,星球内专栏累计输出 150w+ 字,讲解图 5110+ 张,还在持续爆肝中.. 后续还会上新更多项目,已有 4700+ 小伙伴加入学习,欢迎点击围观

面试考察点

  1. 生产经验:面试官不仅仅是想知道一个数字,更是想看你能不能结合实际项目说出 "正常" 和 "异常" 的判断依据。没踩过 GC 坑的人,往往给不出有说服力的回答。

  2. GC 机制理解:你得先搞清楚 Full GC 的触发条件(老年代满了、元空间满了、System.gc() 等),才能理解什么频率是正常的,什么是需要报警的。

  3. 排障能力:追问 "频繁 Full GC 怎么排查" 几乎是必走的路线。面试官想看你的排查思路是否清晰,有没有实战经验。

核心答案

先说结论,最理想的状况是:Full GC 零发生,或者几天甚至几周才一次。 频繁 Full GC 一定是系统有问题的信号。

实际生产环境中的参考标准:

频率 判定 说明
几天甚至更长一次 ✅ 正常 健康状态,老年代增长缓慢,GC 回收效率高
几小时一次 ⚠️ 需关注 可能是内存配置偏小,或者有缓慢的内存泄漏,建议观察趋势
几十分钟一次 ❌ 异常 大概率有问题,需要排查是内存泄漏还是配置不当
几分钟甚至几秒一次 🚨 严重告警 系统基本不可用,必须立即排查

关键不是 "多久一次",而是 "趋势"。如果 Full GC 的间隔越来越短,那就是内存泄漏的前兆,不管当前频率是多少,都必须立即处理。

深度解析

一、为什么说 Full GC "越少越好"?

得先理解 Full GC 有多 "贵"。

上图对比了两种 GC 的差异。核心区别:

  • Young GC 只清理年轻代(Eden + Survivor),年轻代通常比较小(几百 MB),回收速度快,停顿时间在 10-50ms 左右,用户基本感知不到
  • Full GC 要清理 整个堆(年轻代 + 老年代)+ 元空间,涉及的区域大、对象多,STW 停顿时间通常是几百毫秒甚至几秒

几秒的停顿意味着什么?如果你的服务 QPS 是 1000,一个 2 秒的 Full GC 停顿就意味着 2000 个请求在排队等待。用户看到的就是:超时、白屏、loading 转圈圈。

所以 Full GC 越少越好,这不是 "建议",是 "要求"。

二、Full GC 的触发条件

搞清楚 Full GC 为什么会发生,才能理解怎么让它少发生。常见的触发条件有这几个:

1. 老年代空间不足

最常见的 Full GC 原因。对象在年轻代经历多次 Young GC 后还活着,就会晋升到老年代。当老年代被填满时,触发 Full GC。

// 典型场景:大对象直接进入老年代
byte[] bigArray = new byte[10 * 1024 * 1024]; // 10MB 大对象
// 超过 -XX:PretenureSizeThreshold 阈值的大对象,直接在老年代分配
// 老年代被大量大对象占满后 → Full GC

2. 元空间(Metaspace)不足

JDK 8 之后永久代被移除,类元数据存放在本地内存的 Metaspace 中。当 Metaspace 达到 -XX:MaxMetaspaceSize 上限时,也会触发 Full GC。

典型场景:动态代理、CGLIB、JSP 热加载等大量生成类的框架。

3. 显式调用 System.gc()

// 代码里显式调用了这个方法
System.gc(); // 建议 JVM 执行 Full GC(不一定立即执行)

有些 NIO 框架(如 Netty)或 RMI 机制内部会调用 System.gc()。可以通过 -XX:+DisableExplicitGC 参数屏蔽,但不建议无脑关掉,因为有些场景下(如 NIO 的 DirectByteBuffer 回收)依赖它来触发堆外内存回收。

4. CMS 的 Concurrent Mode Failure

使用 CMS 回收器时,如果老年代没有足够的空间来容纳年轻代晋升过来的对象,CMS 会退化为 Serial GC(单线程 Full GC),停顿时间会非常长。这就是所谓的 Concurrent Mode Failure。

三、频繁 Full GC 怎么排查?

这才是面试官最想听的部分。给你一个实战排查思路:

上图展示了排查 Full GC 问题的标准流程。整体分五步走:

  • 第一步:先用 GC 日志确认 Full GC 到底有多频繁、每次耗时多少。这一步是定性的——是 "偶尔来一次" 还是 "每隔几分钟就来"
  • 第二步:判断触发原因。看 GC 日志中的 Full GC 原因(JDK 11+ 的 -Xlog:gc* 会输出详细原因),是老年代满、Metaspace 满、还是 System.gc()
  • 第三步:用 jmap -histo:live 看哪些对象占内存最多,或者用 Arthas dump 堆快照后用 MAT(Memory Analyzer Tool)分析
  • 第四步:根据分析结果定位问题。最常见的是内存泄漏(如 ThreadLocalremove()、静态集合无限增长)或 JVM 参数配置不当(堆太小)
  • 第五步:修复后压测验证,观察 Full GC 频率是否恢复正常

给你一个实战案例。之前有个线上服务,Full GC 从每天 2-3 次慢慢变成每小时一次,GC 日志显示老年代使用率持续增长且回收不掉。用 MAT 分析堆快照后发现,某个 HashMap 被放在了 static 变量里,只往里 put 不 remove,随着运行时间越来越长,这个 Map 越来越大,老年代被撑满后频繁触发 Full GC。修复很简单——加上清理逻辑,Full GC 就恢复了正常。

四、怎么减少 Full GC 的发生?

1. 合理设置堆大小

# 堆太小是 Full GC 频繁的最常见原因
# 根据实际业务需求设置合理的堆大小
-Xms4g -Xmx4g   # 初始和最大堆相同,避免动态扩缩容

2. 调整年轻代比例

# 年轻代太小 → 对象太快晋升到老年代 → 老年代被填满 → Full GC
# 适当增大年轻代比例
-XX:NewRatio=2     # 老年代:年轻代 = 2:1(默认值)
# 或者直接指定年轻代大小
-Xmn2g

3. 设置 Metaspace 大小

# 类加载频繁的场景(如 Groovy、JSP),适当增大 Metaspace
-XX:MetaspaceSize=256m
-XX:MaxMetaspaceSize=512m

4. 选择合适的垃圾回收器

G1 和 ZGC 在减少 Full GC 方面比 CMS 更有优势。G1 通过 Mixed GC(混合回收)在老年代还没满的时候就开始回收,避免老年代被填满后触发 Full GC。

面试高频追问

  1. Young GC 多久一次算正常?

    Young GC 比较频繁是正常的,通常几秒到几十秒一次,具体取决于年轻代大小和对象分配速率。单次 Young GC 停顿时间应该控制在 50ms 以内。如果 Young GC 停顿时间过长(超过 100ms),可能是年轻代太大或者存在大量跨代引用。

  2. 线上 Full GC 导致服务不可用怎么紧急处理?

    短期紧急方案:先重启服务恢复可用,同时保留 GC 日志和堆快照(jmap -dump:format=b,file=heap.hprof <pid>)供后续分析。长期方案:分析堆快照定位内存泄漏或大对象分配问题,调整 JVM 参数,修复代码。

  3. 怎么监控 Full GC?

    三板斧:GC 日志(-Xlog:gc*:file=gc.log)+ JMX 监控(jstat -gcutil)+ 可视化平台(Prometheus + Grafana,配合 jmx_exporter)。设置告警:Full GC 间隔小于 1 小时告警,小于 10 分钟严重告警。

常见面试变体

  • "线上系统频繁 Full GC,你怎么排查?"
  • "Full GC 的触发条件有哪些?"
  • "如何减少 Full GC 的频率?"
  • "Minor GC 和 Full GC 的区别是什么?"

记忆口诀

Full GC 的 "四不" 原则

  • 不慌:先看 GC 日志,确认频率和趋势
  • 不猜:用 MAT / Arthas 分析堆快照,用数据说话
  • 不拖:趋势变差就要处理,别等挂了再救
  • 不忘:修复后压测验证,确保问题真的解决了

总结

Full GC 的正常频率没有绝对标准,但有个明确的判断原则:越少越好,趋势比绝对值更重要。 几天一次是健康的,几小时一次需要关注,几分钟一次必须排查。面试时把 "判断标准 → 触发条件 → 排查思路 → 调优方法" 这条线串起来讲,面试官一看就知道你是真在一线干过的。