十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Java 垃圾回收终极指南:从可达性分析到 ZGC,一篇彻底搞懂 JVM 内存管理

Java 垃圾回收终极指南:从可达性分析到 ZGC,一篇彻底搞懂 JVM 内存管理 别再背八股了从对象存活判定到 7 种收集器从 STW 到染色指针这篇带你真正理解 GCJava 虚拟机JVM的垃圾回收Garbage CollectionGC是其内存管理的核心机制负责自动识别和回收不再使用的对象从而释放内存避免内存泄漏和OutOfMemoryErrorOOM。与 C/C 需要手动管理内存不同Java 通过 GC 自动回收不再使用的对象显著降低了开发者的心智负担但也引入了性能开销和不可预测的停顿时间Stop-The-WorldSTW。今天这篇文章我们从对象存活判定、垃圾回收算法、分代收集模型、7 种垃圾收集器到生产调优实战彻底把 GC 讲透。一、对象存活判定如何识别“垃圾”GC 的第一步是判断哪些对象是“垃圾”需要被回收。Java 采用可达性分析Reachability Analysis算法。1. 引用计数法Java 不用但面试常问给对象添加一个引用计数器被引用一次就 1引用失效就 -1。计数器为 0 时即可回收。缺点无法解决循环引用问题。classA{Bref;}classB{Aref;}AanewA();BbnewB();a.refb;b.refa;anull;bnull;// 即使 a 和 b 都不再被使用它们的引用计数仍然为 1无法回收这就是 Java 不使用引用计数法而采用可达性分析的原因。2. 可达性分析算法GC Roots Tracing从一系列称为“GC Roots”的根对象出发向下搜索走过的路径称为引用链Reference Chain。如果一个对象到 GC Roots 没有任何引用链相连则该对象不可达可以被回收。可作为 GC Roots 的对象包括虚拟机栈栈帧中的本地变量表中引用的对象如方法参数、局部变量方法区中静态属性引用的对象如static变量方法区中常量引用的对象如字符串常量池中的引用本地方法栈中 JNINative 方法引用的对象Java 虚拟机内部的引用如系统类加载器、基本数据类型的 Class 对象等被同步锁synchronized持有的对象3. Java 的 4 种引用类型影响对象存活判断JDK 1.2 之后Java 将引用分为 4 种强度依次递减引用类型类GC 行为典型用途强引用默认永不回收只要存在Object obj new Object()软引用SoftReference内存不足时回收内存敏感缓存如图片缓存弱引用WeakReference下次 GC 即回收WeakHashMap、ThreadLocal虚引用PhantomReference随时可能回收无法通过它获取对象对象回收通知如Cleaner二、四大垃圾回收算法算法核心思想优点缺点适用场景标记-清除Mark-Sweep标记存活对象 → 清除未标记对象实现简单内存碎片严重老年代CMS复制算法Copying内存分两块存活对象复制到另一块无碎片内存利用率 50%新生代默认标记-整理Mark-Compact标记存活对象 → 移向一端 → 清理边界外无碎片内存利用率高对象移动成本大老年代Serial Old、Parallel Old分代收集Generational不同代用不同算法综合优势实现复杂所有现代 JVM为什么 JVM 采用分代收集基于两个“分代假说”弱分代假说大多数对象“朝生夕死”短命。强分代假说活得越久的对象越可能继续存活。因此JVM 将堆分为新生代Young Generation和老年代Old Generation对不同区域采用不同的回收策略。三、分代收集模型与堆内存结构1. 堆内存布局JVM 堆内存Heap ├── 新生代Young Generation—— 占堆的 1/3 │ ├── Eden 区8/10—— 新对象分配区 │ ├── Survivor0From1/10 │ └── Survivor1To1/10 ├── 老年代Old Generation—— 占堆的 2/3 └── 元空间MetaspaceJava 8—— 本地内存类元数据2. 对象分配与晋升流程新对象→ 优先分配在Eden 区。Minor GC新生代 GCEden 区满时触发。存活对象从 Eden Survivor0 → 复制到Survivor1复制算法。清空 Eden 和 Survivor0交换 Survivor0/Survivor1 角色。晋升Promotion对象熬过一定次数的 Minor GC默认 15 次或 Survivor 区放不下 → 晋升到老年代。Major GC / Full GC老年代满时触发通常伴随STWStop-The-World性能开销大。3. Stop-The-WorldSTWGC 过程中为了确保对象引用关系不被改变JVM 会暂停所有应用线程。这就是 STW。Minor GCSTW 时间较短通常 50ms。Full GCSTW 时间较长可能达到秒级是性能优化的重点目标。四、7 种垃圾收集器详解从 Serial 到 ZGC垃圾收集器是回收算法的具体实现。随着 JDK 版本演进收集器经历了从串行 → 并行 → 并发 → 低延迟的进化。1. 速记表7 种收集器一句话对比收集器一句话记忆停顿级别适用场景Serial单线程清扫停下全世界 100ms客户端/小内存100MBParNewSerial 多线程版配合 CMS 新生代 50ms老系统配合 CMSParallel吞吐量优先多线程复制/整理几百 ms~数 s后台计算、批处理CMS并发标记老年代快扫几十 ms低延迟老系统已废弃G1Region 分块逻辑停顿可控 200ms服务端主流JDK 9 默认ZGC染色指针并发几乎不停 10ms大堆16TB低延迟场景ShenandoahBrooks 指针并发压缩 10ms大堆 OpenJDK2. 各收集器详解Serial / Serial Old最古老特点单线程收集GC 时 STW。算法新生代用复制算法老年代用标记-整理算法。场景客户端应用、单 CPU 环境、内存 100MB。启用-XX:UseSerialGCParallel Scavenge / Parallel Old吞吐量优先特点多线程并行收集关注吞吐量CPU 时间用于业务的比例。场景后台计算、批处理任务对停顿时间不敏感。启用-XX:UseParallelGCCMSConcurrent Mark-Sweep已废弃特点并发标记和清除尽量减少 STW 时间。缺点内存碎片问题且无法处理浮动垃圾。状态JDK 9 标记为废弃JDK 14 移除。启用-XX:UseConcMarkSweepGCG1Garbage-FirstJDK 9 默认特点将堆划分为多个Region区域不再区分新生代/老年代物理隔离。通过预测模型控制 MaxGCPauseMillis。算法整体采用标记-整理局部采用复制算法。场景服务端主流堆内存 4GB追求平衡的吞吐量和延迟。启用-XX:UseG1GC -XX:MaxGCPauseMillis200默认 200msZGCZ Garbage CollectorJDK 11特点染色指针Colored Pointers读屏障Load Barrier实现亚毫秒级停顿停顿时间不随堆大小增长。场景大堆内存 16TB、金融交易等对延迟极度敏感的场景。启用-XX:UseZGC -Xmx16g注意JDK 24 默认启用分代 ZGC。ShenandoahOpenJDK特点RedHat 开发Brooks 指针实现并发压缩停顿 10ms。场景大堆 OpenJDK 环境。启用-XX:UseShenandoahGC3. 性能对比实测数据收集器最大停顿吞吐量Serial85 ms92%Parallel220 ms96%CMS28 ms89%G154 ms94%ZGC3 ms99%Shenandoah5 ms98%结论ZGC/Shenandoah 停顿 10ms吞吐量 98%已成为低延迟场景的首选。五、生产环境 GC 调优实战1. 核心 JVM 参数参数作用示例-Xms初始堆大小-Xms4g-Xmx最大堆大小-Xmx4g-XX:MaxGCPauseMillis目标最大停顿时间G1/ZGC-XX:MaxGCPauseMillis50-XX:ConcGCThreads并发 GC 线程数建议不超过逻辑核心数的 1/4-Xlog:gc*GC 日志Java 9 统一日志-Xlog:gc*:gc.log2. 收集器选择建议场景推荐收集器理由微服务 / Web 应用G1平衡吞吐量和延迟JDK 9 默认金融交易 / 实时系统ZGC亚毫秒级停顿批处理 / 大数据计算Parallel吞吐量最高小内存 / 客户端Serial简单高效3. GC 调优最佳实践启用 GC 日志-Xlog:gc*:gc.log便于问题排查。禁用System.gc()避免显式触发 Full GC。合理设置堆大小-Xmx和-Xms设为相同值避免扩容开销。设置合理的停顿目标初始设置为应用 P99 延迟的 70%。监控 GC 频率和耗时使用 GCEasy、GCViewer 等工具分析 GC 日志。六、面试高频考点Q1如何判断一个对象可以回收AJVM 采用可达性分析算法从 GC Roots 出发不可达的对象即可回收。不是引用计数法无法解决循环引用。Q2Minor GC 和 Full GC 有什么区别AMinor GC新生代 GC采用复制算法STW 时间短频率高。Full GC老年代 GC通常伴随 Metaspace 回收STW 时间长应尽量避免。Q3G1 和 CMS 有什么区别ACMS 是老年代并发收集器会产生内存碎片。G1 是基于Region的整堆收集器可控停顿时间且无碎片问题。Q4ZGC 为什么能做到 10ms 的停顿AZGC 通过染色指针Colored Pointers在指针中存储 GC 状态信息配合读屏障Load Barrier让 GC 的大部分工作与应用程序并发执行几乎不暂停应用线程。七、思考题检验是否真的懂了// 问题 1下面这段代码中objA 和 objB 在 System.gc() 时会被回收吗publicclassGCTest{publicObjectinstancenull;publicstaticvoidtestGC(){GCTestobjAnewGCTest();GCTestobjBnewGCTest();objA.instanceobjB;objB.instanceobjA;objAnull;objBnull;System.gc();// objA 和 objB 会被回收吗}}// 问题 2下面这段代码会产生内存碎片的是哪个收集器// A. Serial B. Parallel C. CMS D. G1答案选中下方空白区域查看会被回收。虽然 objA 和 objB 互相引用循环引用但 Java 使用可达性分析而非引用计数法objA 和 objB 都不再被 GC Roots 引用所以会被回收。C. CMS。CMS 使用标记-清除算法会产生内存碎片。Serial 和 Parallel 使用复制/标记-整理算法G1 基于 Region 也避免了碎片问题。总结终极速查表知识点一句话记忆对象存活判定可达性分析GC Roots不是引用计数GC Roots栈帧局部变量、静态变量、常量、JNI 引用、锁持有对象4 种引用强 软 弱 虚强度递减3 大算法标记-清除有碎片、复制50%空间、标记-整理无碎片有移动成本分代模型新生代复制 老年代标记-整理/标记-清除G1Region 分块停顿可控JDK 9 默认ZGC染色指针 读屏障停顿 10ms调优启日志、禁 System.gc()、合理设堆、选对收集器 互动话题你遇到过因为 GC 导致的服务超时或 OOM 吗有没有升级到 G1 或 ZGC 后“真香”的经历欢迎评论区分享如果觉得有收获别忘了点赞、收藏、转发让更多 Javaer 彻底搞懂垃圾回收我们下篇见发布日期2026-08-26
返回列表