GC 怎么判定“该回收谁”:GC Roots、可达性分析、四种引用与回收算法

很多人学 GC 的痛点是:

  • 名词一堆:标记清除、复制、标记整理、分代
  • 但一旦你真遇到"内存回不去",你又不知道该从哪里解释

这篇把 GC 的主线拆成两条:

  • 先判定谁活谁死(可达性分析)
  • 再决定怎么回收(算法/策略)

1. 为什么 JVM 不用引用计数:循环引用是天坑

引用计数思路:

  • 每个对象维护引用次数
  • 变成 0 就回收

问题:

  • 两个对象互相引用,引用次数永远不为 0,但它们可能已经"不可达"

所以 HotSpot 主流使用:

  • 可达性分析(Reachability Analysis)

2. 可达性分析:从 GC Roots 出发,能走到就是活

核心规则:

  • 从一组根对象(GC Roots)出发遍历对象图
  • 能到达的对象都是存活
  • 到达不了的对象就是垃圾

2.1 常见 GC Roots 你至少要认识

  • 线程栈中的引用(局部变量表)
  • 静态变量引用(被类持有的引用)
  • JNI 引用(本地方法栈)
  • 运行中的线程对象、Class 对象等(不同实现有差异)

这也是为什么:

  • 静态集合很容易造成"你以为没用了但其实一直活着"

3. 回收算法:标记清除 / 复制 / 标记整理

3.1 标记清除(Mark-Sweep)

  • 标记出要回收的对象
  • 直接清除

问题:

  • 产生碎片

3.2 复制算法(Copying)

  • 把存活对象复制到另一块连续空间
  • 直接清理整块旧空间

特点:

  • 回收快、无碎片
  • 需要额外空间

常见用法:

  • 新生代(对象存活率低,复制成本低)

3.3 标记整理(Mark-Compact)

  • 标记存活对象
  • 把存活对象往一侧挪,整理成连续空间

特点:

  • 减少碎片
  • 整理有成本

常见用法:

  • 老年代(存活率高,复制成本高)

4. 分代收集:不是算法,是"策略组合"

分代思想来自经验:

  • 大多数对象活不久

所以:

  • 新生代:复制/快速回收
  • 老年代:标记整理(或标记清除 + 处理碎片)

5. 四种引用:为什么你"以为能回收"但没回收

5.1 强引用

  • 默认写法就是强引用
  • 只要强引用还在,通常不会回收

5.2 软引用(SoftReference

  • 内存紧张时才回收
  • 常用于缓存,但别把它当万能缓存方案

5.3 弱引用(WeakReference

  • 下一次 GC 就可能被回收
  • 常见场景:弱引用缓存、ThreadLocal 的 key

5.4 虚引用(PhantomReference

  • 不影响对象生命周期
  • 常用于"对象被回收前做通知/资源清理"这类高级用法

6. 把这些知识落到排障:你该如何解释"内存回不去"

你遇到"Full GC 后内存回不去",你可以按这个顺序解释:

  • 先确认:是不是有 GC Roots 在引用(静态集合/线程栈/ThreadLocal)
  • 再确认:存活对象是否真的很多(业务缓存/大对象)
  • 最后用 heapdump + MAT 找到引用链

7. 总结

  • HotSpot 主要用可达性分析,从 GC Roots 出发判定存活
  • 回收算法:标记清除(碎片)/复制(快)/标记整理(少碎片)
  • 分代是策略组合:新生代与老年代采用不同回收方式
  • 四种引用决定"对象在不同内存压力下是否会被回收"
相关推荐
万亿少女的梦16811 分钟前
基于Spring Boot的游戏交易管理系统设计与实现
java·spring boot·mysql·系统设计·交易管理
闲猫40 分钟前
Agent工程实践:从WorkFlow到多Agent协作的落地笔记
java·服务器·前端
笨蛋不要掉眼泪1 小时前
Java虚拟机:常用参数
java·开发语言·python
亦暖筑序1 小时前
AgentScope-Java 入门:用 Middleware 审计 Agent 调用
java·ai编程·agentscope
你驴我2 小时前
WhatsApp 消息撤回与编辑的幂等性设计实践
java·服务器·前端·后端·python
青山木2 小时前
Hot 100 ---腐烂的橘子
java·数据结构·后端·算法·leetcode·广度优先
未来之窗软件服务2 小时前
计算机考试-快速排序—东方仙盟
数据结构·算法·排序算法·仙盟创梦ide·东方仙盟
小龙报3 小时前
【优选算法】1. 水果成蓝 2.找到字符串中所有字母的异位词
java·c语言·数据结构·数据库·c++·redis·算法
自强的小白3 小时前
重点!起始时间和结束时间与创建时间和修改时间的区别(localdate与LocaldateTIME区别)以及增删改查什么时候设置目前时间(添加和更新员工时)
java
雪的季节3 小时前
Python 线程同步与异步编程 全解析
java·开发语言