引用计数器算法
引用计数器算法是通过一个计数器来实现的:
- 给每个对象添加一个引用计数器
- 如果新增一个引用,那么引用计数器加一
- 如果失去一个引用,那么引用计数器减一
- 如果引用计数器等于零,那么理解为这个对象死亡
这就是引用计数器算法,实现简单并且判定效率高,但是这个算法存在一个致命的问题,会导致内存泄漏。先看下面的例子:
1 public class ReferenceCountAlgorithm {
2
3 private Object instance;
4
5 public static void main(String[] args) {
6 ReferenceCountAlgorithm objA = new ReferenceCountAlgorithm();
7 ReferenceCountAlgorithm objB = new ReferenceCountAlgorithm();
8
9 objA.instance = objB;
10 objB.instance = objA;
11
12 objA = null;
13 objB = null;
14
15 }
16 }
在这个例子里面第一个new ReferenceCountAlgorithm()
- 这个对象在第6行添加了一个引用objA,引用计数器等于1
- 在第10行又添加了一个引用objB.instance,计数器等于2
- 在第12行失去了一个引用objA,引用计数器等于1
同样的第二个new ReferenceCountAlgorithm()对象最终引用计数器也是1。
此时这两个对象都无法被访问到了,但是他们的引用计数器都不是0,所以如果使用引用计数器算法,那么垃圾收集将不会回收这两个对象,从而导致内存泄漏。
可达性分析算法
目前主流的程序语言(Java、C#等)都是通过可达性分析算法来判断对象是否死亡,是否需要被回收的。
可达性算法是通过计算对象是否”可达”来实现的:
- 定义一系列的”GC Roots”作为根节点
- 从根节点开始往下搜索,搜索所走的路径叫做引用链
- 如果一个对象到任何一个根节点都没有引用链,那么判定为不可达
而只有以下几种对象可以作为”GC Roots”根节点:
- 虚拟机栈中引用的对象,也就是正在执行的方法中引用变量指向的对象
- 方法区中静态属性引用的对象,也就是类的静态成员变量引用的对象
- 方法去中常量引用的对象,主要指String和Class类型
- 本地方法栈中Native方法引用的对象
还是上面ReferenceCountAlgorithm类的案例,只有main方法执行结束,两个ReferenceCountAlgorithm对象都会失去引用链,从而被判断为死亡,无论这两个对象是否有互相的引用。
所以我们目前Java采用的判定对象死亡与否的算法就是可达性分析算法。
至于对象被判定为死亡之后,是否会立即被回收,这里的答案是否定的。
被判定为死亡的对象需要经历两次GC的扫描,还被认定为不可达的对象才会被回收,两次扫描的机制可以看一下上一篇文章finalize()方法总结
喜欢这篇文章的朋友,欢迎扫描下图关注公众号lebronchen,第一时间收到更新内容。