jvm06

上节提到了标记。下面来说说标记的事情。是不是被标记了就肯定会被回收呢?我们都知道Object类有一个finalize()方法,所有类都继承了Object类,因此也默认实现了这个方法。 

finalize方法:

      在该对象被回收之前,该对象的finalize()方法会被调用。这里的回收之前指的就是被标记之后,问题就出在这里,有没有一种情况就是原本一个对象开始不在上一章所讲的“关系网”(引用链)中,但是当开发者重写了finalize()后,并且将该对象重新加入到了“关系网”中,也就是说该对象对我们还有用,不应该被回收,但是已经被标记啦,虚拟机的做法是进行两次标记,即第一次标记不在“关系网”中的对象。第二次的话就要先判断该对象有没有实现finalize()方法了,如果没有实现就直接判断该对象可回收;如果实现了就会先放在一个队列中,并由虚拟机建立的一个低优先级的线程去执行它,随后就会进行第二次的小规模标记,在这次被标记的对象就会真正的被回收了。

以上过程可以通过简单的代码示例测试一下finalize方法的简单使用。 

通过简单的测试可以发现,同一个对象的finalize的方法只会被调用一次。

详细测试代码如下:

package test02;

/**
 * finalize方法的测试
 * @Package       test02
 * @Title:        Demo.java
 * @Company:      $
 * @author        BurgessLee 
 * @date          2018年8月20日-下午8:25:00
 * @Description:  $
 */
public class Demo {
	
	//一个类静态变量
	private static Demo demo = null;
	
	public static void main(String[] args) throws InterruptedException {
		demo = new Demo();
		testHelp();
		testHelp();
		//我被调用了
		//我还活着
		//我挂了

	}
	
	public static void testHelp() throws InterruptedException{
		//从关系网中移除
		demo = null;
	
		/**
		 * 通知要进行垃圾回收
		 * 但不一定会执行垃圾回收的方法,只是一个通知而已
		 * 因此在开发的时候不要过多的依赖这个方法,这里只是做一个简单的测试
		 */
		System.gc();
		
		Thread.sleep(2000);
		
		if(demo == null){
			System.out.println("我挂了");
		}else{
			System.out.println("我还活着");
		}
		
	}
	
	@Override
	protected void finalize() throws Throwable {
		super.finalize();
		//重新加入到关系网中
		demo = this;
		System.out.println("我被调用了");
	}
	
}

如果将代码中的休眠2s的一行注释掉,会发现结果会有所变化。原因是:执行finalize()的是一个低优先级的线程,既然是一个新的线程,虽然优先级低了点,但也是和垃圾收集器并发执行的,所以垃圾收集器没必要等这个低优先级的线程执行完才继续执行。也就是说,finalize()方法不一定会在对象第一次标记后执行。用一句清晰易懂的话来说就是:虚拟机确实有调用方法的动作,但是不会确保在什么时候执行完成。因此也就出现了上面输出的结果,对象被回收之后,那个低优先级的线程才执行完。

猜你喜欢

转载自blog.csdn.net/Burgess_Lee/article/details/81877102