搜索站内内容

← 返回文章列表

JVM 学习 02:引用类型、GC Roots 与垃圾回收算法

根据 JVM 笔记梳理可达性分析、四种引用、分代回收与标记清除、复制、标记整理算法。

JasonBackendJavaJVMGCReferenceMemory约 1,150 字大约 4 分钟
云雾在层叠的山岭之间流动

内存回收题很容易被答成「新生代用复制、老年代用标记整理」。这句话有用,但不足以解决排障。更可靠的起点是:对象为什么还活着、回收时会停在哪里、分配速度与回收速度能否平衡。

如何判断对象是否可以回收

现代 JVM 使用可达性分析。它从一组 GC Roots 出发向下遍历;能到达的对象存活,不能到达的对象才有机会回收。常见 GC Roots 包括虚拟机栈中局部变量引用的对象、静态字段引用的对象、JNI 引用,以及 JVM 内部持有的活动对象。

原始 OneNote 笔记中的 GC Roots 与可达性分析示意

原始笔记图示:从 GC Roots 出发的可达性分析,而非简单引用计数。

引用计数无法解决循环引用:两个对象只互相引用、却没有任何外部入口时,计数都不为零,但它们实际已经不可达。可达性分析正是为此而设计。

强引用、软引用、弱引用、虚引用

引用类型回收时机常见用途
强引用仍可达就不会回收普通对象字段、局部变量
软引用内存紧张时可能回收有明确淘汰策略时的缓存补充
弱引用下一次 GC 即可回收WeakHashMap 等弱关联
虚引用不通过它访问对象资源清理与回收跟踪

原始 OneNote 笔记中的四种引用强弱关系

原始笔记图示:四种引用的可达性与回收时机。

软引用不能代替缓存设计。它的回收时机受堆压力和收集器策略影响,命中率不可控;真正的缓存仍应有容量、过期、淘汰和观测指标。弱引用的 key 也不意味着 value 自动没有成本,尤其要留意容器自身是否还通过其他路径持有 value。

垃圾回收算法有哪些

标记—清除先标记存活或待回收对象,再清除目标对象;优点是实现直接,缺点是可能产生碎片。复制算法把存活对象复制到另一块连续区域,再一次清空旧区域;它分配快、无碎片,但需要预留空间,适合多数对象朝生夕灭的年轻代。

标记—整理同样先标记,再把存活对象向一端移动并整理连续空间。它避免碎片,但移动对象的成本更高。分代收集不是第四种基础算法,而是根据对象寿命把不同算法组合使用:年轻代关注高回收率,老年代关注空间稳定与长寿命对象的整理成本。

Minor GC、Major GC、Full GC 的区别

不同资料对 Major GC 的称呼不完全一致,工程上应优先看日志而不是只记术语。通常,年轻代回收影响范围较小;Full GC 往往意味着更广的区域参与回收,停顿风险更高。出现频繁 Full GC 时,要先区分是分配速率过快、晋升失败、老年代不足、元空间压力还是显式 GC。

一次 Full GC 并不自动说明内存泄漏;多轮回收后老年代曲线仍持续抬升,且存活对象集中在同类业务对象、缓存或集合中,才更接近泄漏证据。堆 dump、GC 日志与请求/队列指标应一起看。

常见面试追问:对象什么时候进入老年代

对象可以因为达到年龄阈值、同龄对象在 Survivor 区占比过高、或 Survivor 容纳不下而提前晋升。阈值和实际行为受收集器、堆设置、对象大小与存活分布影响,不应把某个数字当成所有 JVM 的固定结论。

实践中更值得观察的是晋升速率:如果短生命周期对象大量晋升,说明年轻代容量、对象生命周期或分配模式可能不匹配;如果大对象直接进入老年代,则需要检查批量缓冲、序列化和请求体等路径。

小结

GC 不是「定时清空不用的东西」。它围绕可达性做判断,围绕对象寿命和堆布局做取舍。答题或排障时,按 GC Roots → 引用类型 → 分配/晋升 → 回收日志 → 堆证据的顺序推进,会比只背收集器名称可靠得多。

FIELD NOTES / DISCUSS

文章讨论

读完后,欢迎留下你的补充、疑问或不同看法。

240 浏览

全部评论 (0)

正在读取评论…

    GUEST IDENTITY

    设置访客身份

    评论、回复与留言板将复用这份身份。

    选择头像