java对象被判定为可回收需经历两次标记:第一次从gc roots不可达并判断是否执行finalize();第二次若未重建强引用才正式回收;类卸载则须满足实例全回收、类加载器回收及class对象无引用三条件。

Java 中类与对象是否被判定为垃圾,核心不在于“类”本身,而在于“对象实例”是否还被可达。类(Class 对象)作为方法区中的元数据,只有在满足特定条件时才可能被卸载;而普通对象的回收,则完全依赖可达性分析。
对象怎么才算真正可回收
对象不是一“没引用”就立刻被收走,而是要经历两次标记:
- 第一次标记:从 GC Roots 出发不可达,进入“即将回收”队列,并判断是否需执行 finalize() 方法(仅一次机会,且不推荐依赖)
- 第二次标记:若对象在 finalize() 中重新与 GC Roots 建立了强引用(比如把自己赋给某个静态变量),就能逃过回收;否则正式列为可回收对象
注意:即使对象不可达,只要它对应的 Class 对象仍被加载且有活跃引用(如通过反射、动态代理等),该类就不会被卸载,但不影响其对象实例被回收。
哪些对象容易被判定为垃圾
以下情况中创建的对象,通常很快成为回收候选:
- 局部变量作用域结束,如 for 循环内 new 的对象,每次迭代后上一轮对象即失去栈帧引用
- 显式置为 null,如 obj = null;,切断强引用链
- 引用被重新赋值,如 obj1 = obj2;,原 obj1 指向的对象若再无其他引用,即不可达
- 使用 WeakReference 或 PhantomReference 持有的对象,在 GC 发生时会立即断开或入队,不阻止回收
类(Class)什么时候会被回收
类的卸载比对象回收严格得多,必须同时满足三个条件:
- 该类所有实例都已被回收(即堆中无该类对象)
- 加载该类的 ClassLoader 实例已被回收(比如 Web 应用重启时的自定义类加载器)
- 该类对应的 java.lang.Class 对象没有被任何地方引用(包括静态变量、常量池、JNI 引用等)
常见场景是 OSGi、热部署、函数计算(FaaS)等需要频繁加载/卸载类的环境;普通 Spring Boot 应用中,系统类加载器加载的核心类几乎永远不会被卸载。
为什么不用引用计数法
Java 明确弃用引用计数,主因是它无法处理循环引用:
- 两个对象互相持有对方引用,计数器都不为 0,即使外部已无任何引用,也无法回收
- JVM 实测表明,即便写死循环引用代码,GC 依然能正确回收——这反向证明底层用的是可达性分析
可达性分析以 GC Roots 为起点(如栈中局部变量、静态字段、JNI 引用等),只要路径中断,无论内部如何引用,都判定为可回收。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











