1. 对象垃圾回收
如果一个或多个对象没有任何的引用指向它了,那么这个对象现在就是垃圾,如果定位了垃圾,则有可能会被垃圾回收器回收。
如果要定位什么是垃圾,有两种方式来确定:
- 第一个是引用计数法。
- 第二个是可达性分析算法。
1.1 引用计数法
每个对象维护一个引用计数器
当有引用指向该对象时,计数器加1
当引用失效时,计数器减1
当计数器为0时,表示对象可以被回收
示例:
Object A = new Object(); // A的引用计数=1
Object B = A; // A的引用计数=2
A = null; // A的引用计数=1 (B仍引用)
B = null; // A的引用计数=0 (可回收)
优点:
实现简单
可以立即回收垃圾对象
回收操作平摊在程序运行过程中
缺点
无法解决循环引用问题(主要缺点)
计数器维护带来额外开销
多线程环境下需要同步操作计数器
// 循环依赖的情况
class Node {
Node next;
}
Node a = new Node(); // a计数=1
Node b = new Node(); // b计数=1
a.next = b; // b计数=2
b.next = a; // a计数=2
a = null; // a计数=1 (b.next引用)
b = null; // b计数=1 (a.next引用)
// 此时a和b形成循环引用,计数都不为0,但实际已不可达
1.2 可达性分析算法
JVM采用的垃圾回收算法
通过一系列称为"GC Roots"的根对象作为起始点
从这些根对象开始向下搜索,搜索走过的路径称为"引用链"
当一个对象到GC Roots没有任何引用链相连时,则判断为可回收对象
GC Roots包括 :
虚拟机栈(栈帧中的局部变量表)中引用的对象
void method() { Object obj = new Object(); // obj是GC Root } // 方法结束后obj不再为GC Root
方法区中类静态属性引用的对象
class MyClass { static Object staticObj; // staticObj是GC Root }
方法区中常量引用的对象
class MyClass { final static Object CONST_OBJ = new Object(); // GC Root }
本地方法栈中JNI(即Native方法)引用的对象
Java虚拟机内部的引用(如基本类型对应的Class对象)
被同步锁(synchronized)持有的对象
优点
可以解决循环引用问题
更适合面向对象的语言特性
缺点
需要暂停用户线程(Stop The World)进行可达性分析
实现相对复杂
对比总结
特性 | 引用计数法 | 可达性分析算法 |
---|---|---|
实现复杂度 | 简单 | 复杂 |
循环引用处理 | 无法处理 | 可以处理 |
实时性 | 实时回收 | 需要GC触发 |
性能开销 | 计数操作分散 | 集中式GC停顿 |
使用场景 | Python、PHP等 | Java、C#等 |
2. 问题总结
2.1 对象什么时候可以被垃圾器回收?
如果一个或多个对象没有任何的引用指向它了,那么这个对象现在就是垃圾如果定位了垃圾,则有可能会被垃圾回收器回收。
2.2 定位垃圾的方式?
- 引用计数法
- 可达性分析算法