都说无用的对象会被gc,那怎么判断哪些是无用的对象,那些还是有用的对象?
引用计数法
- 给对象增加一个引用计数器,每当有一个地方引用它时,计数器就+1;当引用失效时,计数器就-1;任何时刻计数器为0的对象就是不能再被使用的,即对象已"死"。
- 引用计数法实现简单,判定效率也比较高,在大部分情况下都是一个不错的算法。比如Python语言就采用引用计
数法进行内存管理。 - 但是,在主流的JVM中没有选用引用计数法来管理内存,最主要的原因就是引用计数法
无法解决对象的循环引用
问题
可达性分析算法
GC Roots根
可达性分析算法
此算法的核心思想为 : 通过一系列称为"GC Roots"的对象作为起始点,从这些节点开始向下搜索,搜索走过的路径
称之为"引用链",当一个对象到GC Roots没有任何的引用链相连时(从GC Roots到这个对象不可达)时,证明此对象
是不可用的。
对象Object5-Object7之间虽然彼此还有关联,但是它们到GC Roots是不可达的,因此他们会被判定为可回收对
象
对象自我拯救
要宣告一个对象的真正死亡,至少要经历两次标记过程 : 如果对象在进行可达性分析之后发现没有与GC Roots相连接的引用链,那它将会被第一次标记并且进行一次筛选,筛选的条件是此对象是否有必要执行finalize()方法。当对象没有覆盖finalize()方法或者finalize()方法已经被JVM调用过,虚拟机会将这两种情况都视为"没有必要执行",此时的对象才是真正"死"的对象 .
public class Test {
public static Test test;
public void isAlive() {
System.out.println("I am alive :)");
}
@Override
protected void finalize() throws Throwable {
super.finalize();
System.out.println("finalize method executed!");
test = this;
}
public static void main(String[] args)throws Exception {
test = new Test();
test = null;
System.gc();
Thread.sleep(500);
if (test != null) {
test.isAlive();
}else {
System.out.println("no,I am dead :(");
} // 下面代码与上面完全一致,但是此次自救失败
test = null;
System.gc();
Thread.sleep(500);
if (test != null) {
test.isAlive();
}else {
System.out.println("no,I am dead :(");
}
}
}
- 从上面代码示例我们发现,finalize方法确实被JVM触发,并且对象在被收集前成功逃脱。
- 但是从结果上我们发现,两个完全一样的代码片段,结果是一次逃脱成功,一次失败。这是因为,
任何一个对象的finalize()方法都只会被系统自动调用一次
,如果相同的对象在逃脱一次后又面临一次回收,它的finalize()方法不被再次执行,因此第二段代码的自救行动失败。