JVM 是如何判断一个对象是“垃圾”的?(引用计数 vs 可达性分析)
·
• 引用计数:每次引用计数一下,没有引用就可以释放(需要添加额外的逻辑来处理循环依赖引用)→Python
• 可达性分析:从ROOT对象开始搜索,搜不到的就都可以回收→Java
- 引用计数法 (Reference Counting): 给对象添加引用计数器。缺点: 无法解决循环引用问题,Java 不使用,但是Python使用的是这种方法。因此在后面出现循环依赖等等现象的时候Python花费了更多的思路去解决这种问题
- 可达性分析法 (Reachability Analysis): Java 采用的方法。从 GC Roots 开始向下搜索,如果一个对象到 GC Roots 没有任何引用链相连,则认为此对象不可达,可以回收
哪些可以作为 GC Roots?
GC Root 就是那些"当前一定活着、不可能被回收"的起点引用
- 虚拟机栈中引用的对象(局部变量)
- 这个方法正在执行中,栈帧还在调用栈上,因此相关的变量不可以回收否则就影响栈里面运行的方法了
- 方法区中类静态属性、常量引用的对象
- 静态变量的生命周期跟类绑定,只要类没被卸载,静态变量就一直存在,类卸载的条件极其苛刻(类加载器被回收等),绝大多数情况下类不会被卸载,所以静态变量引用的对象在程序运行期间几乎永远可达
- 常量(
static final)一旦赋值就永远不会改变。跟静态变量类似,生命周期跟类绑定,常量引用的对象在类存活期间一定不能被回收
- 本地方法栈中 JNI 引用的对象
- JNI 是 Java 调用 C/C++ 的桥梁,本地代码正在执行中。因为JVM 看不到 C 代码内部的逻辑,不知道本地代码什么时候会用这个对象。所以只要 JNI 持有引用,JVM 就必须假设它在被使用,不能回收
- 所有被同步锁(synchronized)持有的对象
- 锁对象正在被线程持有,用于同步控制。如果锁对象被回收了,其他等待这个锁的线程就永远无法被唤醒,整个同步机制崩溃,所以 JVM 必须保证锁对象在同步块执行期间存活
为什么堆里的普通对象不是 GC Root?
class Node {
Node next;
}
// 两个对象互相引用,但没有任何 GC Root 能到达它们
因为堆里的对象可能只是互相引用(循环引用),但没有任何"活着的线程/类/锁"在用它们。如果让堆对象当 Root,循环引用就永远回收不了。
更多推荐

所有评论(0)