一、对象已经死了吗?
在堆里面存放着Java世界中几乎所有的对象实例,垃圾收集器在对堆进行回收前,第一件事情就是确定这些对象之中哪些是“存活”着,哪些已经“死去”(即不可能再被任何途径使用的对象)
判断对象是否存活的算法:引用计数法、可达性分析算法。这里分两篇博文介绍一下这两种算法,本篇先介绍一下:引用计数算法
二、引用计数算法
引用计数算法:给对象中添加一个引用计数器,每当有一个地方引用它时,计数器值就+1;当引用失效时,计数器值就-1;任何时刻计数器为0的对象就是不能再被使用的垃圾对象。
引用计数算法的实现简单,判定效率高。在大部分情况下它都是一个不错的算法。但是,至少主流的Java虚拟机里面没有选用引用计数算法来管理内存,主要原因是它很难解决对象之间的相互循环引用的问题,这种情况下,即使断开了对象在虚拟机栈中的reference,引用计数器永远都不会为0,这样就会造成内存泄漏。下面举例说明:
下面代码中对象g1和g2都有字段instance,赋值使g1.instance = g2 g2.instance = g1 除此之外,这两个对象再无任何引用,实际上这两个对象已经不可能再被访问,但是它们之间相互引用着对方,导致它们的引用计数都不为0,于是引用计数算法无法通知GC收集器回收它们。
package cc.mynatapp.detroitdentist.gc;
/**
* @author Xu hao
* @Description 判断对象是否存活算法:引用计数
* 需要设置JVM参数:
* -verbose:gc 打印垃圾回收信息
* -xx:+PrintGCDetails 打印垃圾回收的详细信息
* @Version 1.0
* Email [email protected]
* create on 2018/9/22
*/
public class GCStrategyRefCount {
private Object instance;
/**
* 这里构造器的唯一意义就是占用点内存,以便能在GC日志中看清楚是否被回收过。
**/
public GCStrategyRefCount() {
byte [] g = new byte[20 * 1024 * 1024];
}
public static void main(String[] args){
GCStrategyRefCount g1 = new GCStrategyRefCount();
GCStrategyRefCount g2 = new GCStrategyRefCount();
// g1 g2 循环引用
g1.instance = g2;
g2.instance = g1;
// 断掉Java栈中g1 g2的reference
g1 = null;
g2 = null;
System.gc();
}
}
运行结果如下:
从上面的结果中可以清楚看到,GC日志中包含“21054K->574K(31428K)”,21054-574=20480(刚好20M)这意味着虚拟机并没有因为这两个对象相互引用就不回收它们,这也从侧面说明HotSpot并没有通过引用计数算法来判断对象是否为垃圾对象(对象是否存活)。