FEATURED · 精选文章

Full GC 详解与排查方案:从原理到实战

发布时间 / 2026/8/29 0:54:29
来源 / 创域科博编辑部
栏目 / 资讯中心
Full GC 详解与排查方案:从原理到实战 1. 引言Full GCFull Garbage Collection全量垃圾回收是 JVM 垃圾回收中最令人头疼的话题之一。在 Java 应用运行过程中Full GC 一旦频繁发生往往伴随着明显的停顿Stop-The-WorldSTW、CPU 飙升、接口超时等问题严重时甚至会导致线上事故。本文将从 Full GC 的基本概念入手逐步深入其触发机制、常见原因并给出系统化的排查思路与实战方案帮助你在遇到 Full GC 问题时能够快速定位、从容应对。2. 什么是 Full GC2.1 垃圾回收的基本概念JVM 的内存被划分为多个区域其中堆内存Heap是垃圾回收的主要战场。堆内存又细分为新生代Young Generation存放新创建的对象进一步分为 Eden 区和两个 Survivor 区S0、S1。老年代Old Generation存放存活时间较长的对象或从新生代晋升过来的大对象。垃圾回收器会定期扫描堆内存回收不再被引用的对象释放内存空间。2.2 Full GC 的定义Full GC 是指对整个堆内存新生代 老年代 元空间/Metaspace进行的一次完整垃圾回收。与 Minor GC只回收新生代和 Major GC只回收老年代不同Full GC 涉及范围最广耗时也最长。2.3 Full GC 与 Minor GC 的区别对比项Minor GCFull GC回收范围仅新生代整个堆新生代 老年代 元空间触发频率高低停顿时间短毫秒级长秒级甚至分钟级对业务影响较小极大可能导致接口超时2.4 Full GC 的停顿STWFull GC 期间JVM 会暂停所有应用线程进入 Stop-The-World 状态。这意味着在这段时间内业务请求无法被处理所有线程都处于阻塞状态。停顿时间的长短取决于堆内存的大小、存活对象的数量以及所使用的垃圾回收器。3. Full GC 的触发机制了解 Full GC 的触发条件是排查问题的第一步。以下是 Full GC 常见的触发场景3.1 老年代空间不足这是最常见的触发原因。当老年代被占满无法容纳新晋升的对象时JVM 会触发 Full GC。具体场景包括大对象直接进入老年代导致老年代空间快速耗尽。长期存活的对象过多新生代晋升频繁。内存泄漏导致老年代对象只增不减。3.2 元空间Metaspace不足JDK 8 之后方法区被元空间取代。当加载的类过多、动态生成代理类过多时元空间可能被占满从而触发 Full GC。3.3 晋升失败Promotion Failure当新生代进行 Minor GC 时如果存活对象无法放入 Survivor 区需要晋升到老年代但此时老年代空间不足就会发生晋升失败进而触发 Full GC。3.4 大对象直接分配当创建的对象大小超过-XX:PretenureSizeThreshold阈值时对象会直接进入老年代。如果短时间内大量创建大对象老年代空间会迅速耗尽触发 Full GC。3.5 显式调用 System.gc()代码中显式调用System.gc()会触发 Full GC尽管只是建议但大多数垃圾回收器都会响应。一些框架如 RMI、NIO在特定场景下也会触发显式 GC。3.6 CMS 并发模式失败使用 CMS 垃圾回收器时如果并发回收期间老年代被占满会触发 Concurrent Mode Failure退化为 Serial Old 进行 Full GC。4. Full GC 的常见原因分析4.1 内存泄漏内存泄漏是 Full GC 频繁发生的头号元凶。对象被无意中持有引用无法被回收导致老年代持续增长。常见的内存泄漏场景包括静态集合类持有对象引用未及时清理。连接数据库、HTTP、Redis未关闭。监听器、回调未注销。ThreadLocal 使用不当导致内存泄漏。4.2 对象分配速率过高如果应用在短时间内大量创建对象新生代频繁触发 Minor GC存活对象不断晋升到老年代最终导致老年代空间不足触发 Full GC。4.3 堆内存配置不合理堆内存设置过小无法满足应用的实际需求。新生代与老年代比例不当导致对象过早晋升。大对象阈值设置不合理。4.4 元空间配置不足动态生成大量类如 CGLIB 代理、反射、热部署时如果元空间上限设置过小会频繁触发 Full GC。4.5 代码中显式调用 System.gc()某些第三方框架或业务代码中调用了System.gc()导致 Full GC 频繁发生。5. Full GC 的排查方案5.1 排查前的准备工作在开始排查之前需要确保以下几点开启 GC 日志-XX:PrintGCDetails -XX:PrintGCDateStamps -Xloggc:/path/to/gc.log开启堆转储-XX:HeapDumpOnOutOfMemoryError -XX:HeapDumpPath/path/to/dump.hprof监控 CPU、内存、磁盘 IO 等系统指标。5.2 查看 GC 日志GC 日志是排查 Full GC 的第一手资料。通过分析 GC 日志可以了解Full GC 发生的频率和时间点。Full GC 前后的堆内存使用情况。Full GC 的停顿时间。# 查看 GC 日志中 Full GC 的记录grepFull GCgc.log|head-205.3 使用 jstat 实时监控jstat是 JDK 自带的监控工具可以实时查看 JVM 的内存使用和 GC 情况。# 每 1 秒输出一次 GC 统计信息共输出 10 次jstat-gcutilpid100010重点关注FGCFull GC 次数和FGCTFull GC 累计耗时两列。5.4 使用 jmap 分析堆内存当怀疑内存泄漏时可以使用jmap生成堆转储文件再用 MATMemory Analyzer Tool或 VisualVM 分析。# 生成堆转储文件jmap-dump:formatb,fileheap.hprofpid5.5 使用 jvisualvm 可视化分析jvisualvm是 JDK 自带的图形化监控工具可以直观地查看堆内存使用趋势、GC 情况以及线程状态。5.6 使用 Arthas 在线诊断Arthas 是阿里巴巴开源的 Java 诊断工具可以在不重启应用的情况下进行在线诊断。# 查看 GC 情况dashboard# 查看类加载情况classloader# 查看内存使用memory5.7 排查步骤总结确认 Full GC 频率通过 GC 日志或 jstat 确认 Full GC 发生的频率和规律。分析 GC 日志查看 Full GC 前后的内存变化判断是哪个区域空间不足。定位内存增长点使用 jmap 生成堆转储用 MAT 分析大对象和引用链。检查代码排查内存泄漏、大对象创建、System.gc() 调用等问题。调整 JVM 参数根据分析结果调整堆大小、GC 策略等参数。验证效果调整后持续观察确认 Full GC 频率恢复正常。6. 实战案例6.1 案例一内存泄漏导致 Full GC 频繁现象某订单系统每天下午 Full GC 频繁接口响应变慢。排查过程查看 GC 日志发现 Full GC 频率从每小时 1 次增加到每 10 分钟 1 次。使用jmap生成堆转储用 MAT 分析发现HashMap中持有大量订单对象引用。定位到代码发现一个静态缓存 Map 在订单完成后未移除订单对象。解决方案在订单完成后主动从缓存中移除对象并使用弱引用替代强引用。6.2 案例二大对象导致老年代空间不足现象某报表系统每次生成报表时都会触发 Full GC。排查过程通过 GC 日志发现Full GC 发生在报表生成期间。使用 Arthas 的memory命令发现老年代空间在报表生成时迅速增长。定位到代码发现报表数据一次性加载到内存中生成了大量大对象。解决方案将报表数据改为分批加载并调大-XX:PretenureSizeThreshold阈值避免大对象直接进入老年代。7. JVM 参数调优建议7.1 堆内存设置# 设置堆内存大小-Xms4g-Xmx4g# 设置新生代大小-Xmn2g# 设置新生代与老年代比例-XX:NewRatio27.2 垃圾回收器选择# JDK 8 使用 G1-XX:UseG1GC# JDK 11 使用 ZGC-XX:UseZGC7.3 其他常用参数# 设置大对象直接进入老年代的阈值-XX:PretenureSizeThreshold1m# 设置元空间大小-XX:MetaspaceSize256m-XX:MaxMetaspaceSize512m# 禁用显式 GC-XX:DisableExplicitGC8. 总结Full GC 是 Java 应用运行中不可避免的现象但频繁的 Full GC 往往意味着代码或配置存在问题。排查 Full GC 的核心思路是先看日志通过 GC 日志确认 Full GC 的频率和内存变化。再抓堆转储用 MAT 分析内存中的大对象和引用链。后查代码定位内存泄漏、大对象创建等根因。最后调参根据分析结果合理调整 JVM 参数。掌握 Full GC 的原理和排查方法是每个 Java 开发者进阶的必修课。希望本文能帮助你在面对 Full GC 问题时不再手足无措而是能够从容应对、快速解决。
RELATED — 相关阅读

相关资讯

LATEST — 最新资讯

最新发布

TODAY — 本日精选

新闻

WEEKLY — 本周精选

新闻

MONTHLY — 本月精选

新闻