首页 > 代码库 > 深入理解Java虚拟机系列——垃圾回收器与内存分配策略(二)

深入理解Java虚拟机系列——垃圾回收器与内存分配策略(二)

判断对象是否存活的算法:

简单版:给对象添加一个引用计数器,每当有一个地方引用它时,计数器值就加1,当引用失效时,计数器值就减1。任何时刻计数器为0的对象就是不可能再被使用的。

但主流的Java虚拟机都没有引用计数算法来管理内存,最重要的原因就是它很难解决对象之间相互循环引用的问题。

可达性分析算法(Reachability Analysis):

在主流的程序语言(Java,C#等)的主流实现中,都是通过可达性分析来判定对象是否存活。

算法思想:通过一系列的成为"GC Roots"的对象作为起始点,从这些节点开始向下搜索,搜索所走过的路径称为引用链(Reference Chain),当一个对象到GC Roots没有任何引用链相连时,则证明此对象是不可用的。

在Java语言中,可作为GC Roots的对象包括以下几种:

1)虚拟机栈(栈帧中的本地变量表)中引用的对象;

2)方法区中类静态属性引用的对象;

3)方法中常量引用的对象;

4)本地方法中JNI(即一般说的Native方法)引用的对象。

Java中的引用定义:如果reference类型的数据中存储的数值代表的是另一块内存的起始地址,就称为这块内存代表着一个引用。

JDK1.2之后,Java对引用的概念进行了扩充,将引用分为强引用(Strong Reference)、软引用(Soft Reference)、弱引用(Weak Reference)、虚引用(Phantom Reference),4种引用强度一次逐渐减弱。

1)强引用:程序代码之中普遍存在的。只要强引用存在,垃圾收集器永远不会回收掉被引用的对象;

2)软引用:用来描述一些还有用但并非必须的对象。对于软引用关联着的对象,在系统将要发生内存溢出异常之前,将会把这些对象列进回收范围之中进行第二次回收。如果这次回收还没有足够的内存,才会抛出内存溢出异常。

3)弱引用:同样是用来描述非必须对象的,强度比软引用更弱一些,被弱引用关联的对象只能生存到下一次垃圾收集发生之前。当垃圾收集器工作时,无论当前内存是否足够,都会回收掉被弱引用关联的对象。

4)虚引用:也称为幽灵引用或幻影引用,一个对象是否有虚引用的存在完全不会对其生存时间构成影响。为一个引用设置虚引用关联的唯一目的就是能在这个对象被收集器回收时收到一个系统通知。

生存还是死亡

要真正宣告一个对象死亡,至少要经历两次标记过程:如果对象在进行可达性分析后发现没有与GC Roots相连接的引用链,那它将会被第一次标记并进行第一次筛选,筛选的条件是此对象是否有必要执行finalize()方法。当对象没有覆盖finalize()方法,或者finalize()方法已经被虚拟机调用过,则虚拟机将这两种情况都视为"没有必要执行"。

程序清单3-2

package secondChapter;
/*
 * 此代码演示了两点:
 * 1.对象可以在被GC时自我救赎
 * 2.这种自救的机会只有一次,因为一个对象的finalize()方法最多只会被系统自动调用一次
*/
public class FinalizeEscapeGC {
	public static FinalizeEscapeGC SAVE_HOOK=null;
	public void isAlive(){
		System.out.println("yes,I am still alive:)");
	}
	@Override
	protected void finalize() throws Throwable{
		super.finalize();
		System.out.println("finalize method executed!");
		FinalizeEscapeGC.SAVE_HOOK=this;
	}
    public static void main(String[] args) throws Throwable{
    	SAVE_HOOK=new FinalizeEscapeGC();
    	//对象第一次成功拯救自己
    	SAVE_HOOK=null;
    	System.gc();
    	//因为finalize方法优先级很低,所以暂停0.5秒等待它
    	Thread.sleep(500);
    	if(SAVE_HOOK!=null){
    		SAVE_HOOK.isAlive();
    	}else{
    		System.out.println("no,i am dead:(");
    	}
    	//下面这段代码与上面完全相同,但是在这次自救确失败了
    	//对象第一次成功拯救自己
    	SAVE_HOOK=null;
    	System.gc();
    	//因为finalize方法优先级很低,所以暂停0.5秒等待它
    	Thread.sleep(500);
    	if(SAVE_HOOK!=null){
    		SAVE_HOOK.isAlive();
    	}else{
    		System.out.println("no,i am dead:(");
    	}
    	
    }
}

  P67

 

深入理解Java虚拟机系列——垃圾回收器与内存分配策略(二)