`
zhang_xzhi_xjtu
  • 浏览: 522123 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
社区版块
存档分类
最新评论

深入理解ReferenceQueue GC finalize Reference

    博客分类:
  • java
阅读更多
关于对象如何销毁以及finalize更详细的信息

目录

概述
1 先看一个对象finalize的顺序问题。
2 对象再生及finalize只能执行一次
3 SoftReference WeakReference
4 PhantomReference
5 ReferenceQueue
Q&A


概述

先说一些基本的东西,GC只负责对象内存相关的清理,其他资源如文件句柄,db连接需要手动清理,以防止系统资源不足崩溃。System.gc()只是建议jvm执行GC,但是到底GC执行与否是由jvm决定的。

一个正常的对象的生命周期。

当新建一个对象时,会置位该对象的一个内部标识finalizable,当某一点GC检查到该对象不可达时,就把该对象放入finalize queue(F queue),GC会在对象销毁前执行finalize方法并且清空该对象的finalizable标识。

简而言之,一个简单的对象生命周期为,Unfinalized Finalizable Finalized Reclaimed。

Reference中引用的object叫做referent。

1 先看一个对象finalize的顺序问题。

	public class A {
		B b;
		public void finalize() {
			System.out.println("method A.finalize at " + System.nanoTime());
		}
	}

	public class B {
		public void finalize() {
			System.out.println("method B.finalize at " + System.nanoTime());
		}
	}

		A a = new A();
		a.b = new B();
		a = null;
		System.gc();


按照http://java.sun.com/developer/technicalArticles/javase/finalization/
所说,对象a在finalize之前会保持b的引用,但是实验中对象a和a中的对象b的finalize方法运行时间有先有后,而且大部分时间里,a的finalize方法的执行时间是晚于b的finalize方法的。我记着java编程语言书中说是一切可以finalize的对象的finalize方法的执行顺序是不确定的。到底应该听谁的?最好的实践就是不要依赖finalize的顺序或者写一些防御代码。

【note】我仍然坚持最好的实践就是不要依赖finalize的顺序或者写一些防御代码。但是通过进一步的学习和实验,因为a有可能复活,所以在a没有决定到底复活不复活之前b是不会被回收的。控制台的顺序问题应该是多线程的问题导致的。
【note】查看了JLS后,确定了finalize是乱序执行的。

2 对象再生及finalize只能执行一次

	public class B {

		static B b;

		public void finalize() {
			System.out.println("method B.finalize");
			b = this;
		}
	}

		B b = new B();
		b = null;
		System.gc();
		B.b = null;
		System.gc();

对象b本来已经被置null,GC检查到后放入F queue,然后执行了finalize方法,但是执行finalize方法时该对象赋值给一个static变量,该对象又可达了,此之谓对象再生。

后来该static对象也被置null,然后GC,可以从结果看到finalize方法只运行了1次。为什么呢,因为第一次finalize运行过后,该对象的finalizable置为false了,所以该对象即使以后被gc运行,也不会执行finalize方法了。

很明显,对象再生是一个不好的编程实践,打乱了正常的对象生命周期。但是如果真的需要这么用的话,应该用当前对象为原型重新生成一个对象使用,这样以后这个新的对象还可以被GC运行finalize方法。

3 SoftReference WeakReference

SoftReference会尽量保持对referent的引用,直到JVM内存不够,才会回收SoftReference的referent。所以这个比较适合实现一些cache。

WeakReference不能阻止GC对referent的处理。


4 PhantomReference

幻影引用,幽灵引用,呵呵,名字挺好听的。

奇特的地方,任何时候调用get()都是返回null。那么它的用处呢,单独好像没有什么大的用处,所以要结合ReferenceQueue。

5 ReferenceQueue

ReferenceQueue WeakReference PhantomReference都有构造函数可以传入ReferenceQueue来监听GC对referent的处理。

	public class A {
	}

		ReferenceQueue queue = new ReferenceQueue();
		WeakReference ref = new WeakReference(new A(), queue);
		Assert.assertNotNull(ref.get());

		Object obj = null;
		obj = queue.poll();
		Assert.assertNull(obj);

		System.gc();

		Assert.assertNull(ref.get());
		obj = queue.poll();
		Assert.assertNotNull(obj);


分析,在GC运行时,检测到new A()生成的对象只有一个WeakReference引用着,所以决定回收它,首先clear WeakReference的referent,然后referent的状态为finalizable,同时或者一段时间后把WeakReference放入监听的ReferenceQueue中。

注意有时候最后的Assert.assertNotNull(obj);有时会失败,因为还没有来的及把WeakReference放入监听的ReferenceQueue中。

换成PhantomReference试试,

		ReferenceQueue queue = new ReferenceQueue();
		PhantomReference ref = new PhantomReference(new A(), queue);

		Assert.assertNull(ref.get());

		Object obj = null;
		obj = queue.poll();

		Assert.assertNull(obj);

		System.gc();

		Thread.sleep(10000);

		System.gc();

		Assert.assertNull(ref.get());
		obj = queue.poll();
		Assert.assertNotNull(obj);


貌似和WeakReference没有什么区别呀,别急,还是有个细微的区别的,SoftReference和WeakReference在GC对referent状态改变时,先clear SoftReference/WeakReference对referent的引用,对应的referent状态为Finalizable,只是可以放入F queue,然后把SoftReference/WeakReference放入ReferenceQueue。

而PhantomReference当GC对referent的状态改变时,在把PhantomReference放入ReferenceQueue之前referent已经被GC处理到Reclaimed了,即该referent被销毁了。


搞了这么多,有什么用?可以使用PhantomReference更好的控制一些关于对象生命周期的事情,当WeakReference放入ReferenceQueue时,并不能保证该referent是被销毁了。别忘了对象可以在finalize方法里再生。而使用PhantomReference,当在ReferenceQueue中发现PhantomReference时,可以保证referent已经被销毁了。

	public class A {
		static A a;
		public void finalize() {
			a = this;
		}
	}

		ReferenceQueue queue = new ReferenceQueue();

		WeakReference ref = new WeakReference(new A(), queue);

		Assert.assertNotNull(ref.get());

		Object obj = null;

		obj = queue.poll();

		Assert.assertNull(obj);

		System.gc();

		Thread.sleep(10000);

		System.gc();

		Assert.assertNull(ref.get());

		obj = queue.poll();

		Assert.assertNotNull(obj);


即使new A()出来的对象再生了,在queue中还是可以看到WeakReference。

		ReferenceQueue queue = new ReferenceQueue();

		PhantomReference ref = new PhantomReference(new A(), queue);

		Assert.assertNull(ref.get());

		Object obj = null;

		obj = queue.poll();

		Assert.assertNull(obj);

		// 第一次gc

		System.gc();

		Thread.sleep(10000);

		System.gc();

		Assert.assertNull(ref.get());

		obj = queue.poll();

		Assert.assertNull(obj);

		A.a = null;

		// 第二次gc

		System.gc();

		obj = queue.poll();

		Assert.assertNotNull(obj);


第一次gc后,由于new A()的对象再生了,所以queue是空的,因为对象没有销毁。

当第二次gc后,new A()的对象销毁以后,在queue中才可以看到PhantomReference。

所以PhantomReference可以更精细的对对象生命周期进行监控。


Q&A

Q1:有这样一个问题,为什么UT会Fail?不是说对象会重生吗,到底哪里有问题?

public class Test {

	static Test t;

	@Override
	protected void finalize() {
		System.out.println("finalize");
		t = this;
	}
}

	public void testFinalize() {
		Test t = new Test();
		Assert.assertNotNull(t);
		t = null;
		System.gc();
		Assert.assertNull(t);
		Assert.assertNotNull(Test.t);
	}


A: 对象是会重生不错。
这里会Fail有两个可能的原因,一个是gc的行为是不确定的,没有什么会保证gc运行。呵呵,我承认,我在console上看到东西了,所以我知道gc运行了一次。
另一个问题是gc的线程和我们跑ut的线程是两个独立的线程。即使gc线程里对象重生了,很有可能是我们跑完ut之后的事情了。这里就是时序问题了。

	public void testFinalize() throws Exception {
		Test t = new Test();
		Assert.assertNotNull(t);
		t = null;
		System.gc();
		Assert.assertNull(t);

		// 有可能fail.
		Assert.assertNull(Test.t);
		// 等一下gc,让gc线程的对象重生执行完。
		Thread.sleep(5000);
		// 有可能fail.
		Assert.assertNotNull(Test.t);
	}

这个ut和上面那个大同小异。

一般情况下,code执行到这里,gc的对象重生应该还没有发生。所以我们下面的断言有很大的概论是成立的。
		// 有可能fail.
		Assert.assertNull(Test.t);

让ut的线程睡眠5秒,嗯,gc的线程有可能已经执行完对象重生了。所以下面这行有可能通过测试。
Assert.assertNotNull(Test.t);


嗯,测试通过。但是没有人确保它每次都通过。所以我两处的注释都声明有可能fail。
这个例子很好的说明了如何在程序中用gc和重生的基本原则。
依赖gc会引入一些不确定的行为。
重生会导致不确定以及有可能的时序问题。
所以一般我们不应该使用gc和重生,但是能深入的理解这些概念又对我们编程有好处。

这两个测试如果作为一个TestSuite跑的话,情况又会有不同。因为第一个测试失败之后和第二个测试执行之间,gc执行了对象重生。如此,以下断言失败的概率会升高。
		// 有可能fail.
		Assert.assertNull(Test.t);


To luliruj and DLevin
首先谢谢你们的回复,这个帖子发了好久了,竟然还有人回复。
reclaimed的问题可以参看本帖上边的URL。
关于finalize和ReferenceQueue和关系,主贴已经解释了,luliruj给出了不同的解释。
这个地方我们可以用小程序验证一下.
public class Tem {

	public static void main(String[] args) throws Exception {

		ReferenceQueue queue = new ReferenceQueue();
		// SoftReference ref = new SoftReference(new B(), queue);
		// WeakReference ref = new WeakReference(new B(), queue);
		PhantomReference ref = new PhantomReference(new B(), queue);
		while (true) {
			Object obj = queue.poll();
			if (obj != null) {
				System.out.println("queue.poll at " + new Date() + " " + obj);
				break;
			}
			System.gc();
			System.out.println("run once.");
		}

		Thread.sleep(100000);
	}

}

class B {

	@Override
	protected void finalize() throws Throwable {
		System.out.println("finalize at " + new Date());
	}
}


在classB的finalize上打断点,然后让ref分别为SoftReference/WeakReference/PhantomReference,可以看到。
SoftReference/WeakReference都是不需要finalize执行就可以enqueue的。这个就否掉了luliruj所说的

当 heap 对象的 finalize() 方法被运行而且该对象占用的内存被释放时, WeakReference 对象就被添加到它的 ReferenceQueue (如果后者存在的话)

PhantomReference必须等待finalize执行完成才可以enqueue。
这个正如主贴所说:
而PhantomReference当GC对referent的状态改变时,在把PhantomReference放入ReferenceQueue之前referent已经被GC处理到Reclaimed了,即该referent被销毁了。
12
0
分享到:
评论
24 楼 在世界的中心呼喚愛 2016-09-26  
在世界的中心呼喚愛 写道
public class A {  
    static A a;  
    public void finalize() {  
        a = this;  
    }  
}  
  
    ReferenceQueue queue = new ReferenceQueue();  
  
    WeakReference ref = new WeakReference(new A(), queue);  
  
    Assert.assertNotNull(ref.get());  
  
    Object obj = null;  
  
    obj = queue.poll();  
  
    Assert.assertNull(obj);  
  
    System.gc();  
  
    Thread.sleep(10000);  
  
    System.gc();  
  
    Assert.assertNull(ref.get());  
  
    obj = queue.poll();  
  
    Assert.assertNotNull(obj);  


这个代码有问题吧。。
既然对象销毁又再生,怎么会进入队列?
new A()这里放到外面实例化下:
ReferenceQueue queue = new ReferenceQueue();  
		A a=new A();
		System.out.println("a hashcode = " +a.hashCode());
	    WeakReference ref = new WeakReference(a, queue);  
	  
	    System.out.println(ref.get()); 
	    Object obj = null;  
	    obj = queue.poll();  
	    System.out.println("1=" +obj);  
	  
	    System.gc();  
	    Thread.sleep(10000);  
	    System.gc();  
	  
	    System.out.println("2=" +ref.get());  
	    obj = queue.poll();  
	    System.out.println("hashcode="+a.hashCode()+"  3=" +obj);  




大半夜,眼都花了,a忘记赋值null了
23 楼 在世界的中心呼喚愛 2016-09-26  
在世界的中心呼喚愛 写道
在世界的中心呼喚愛 写道
在classB的finalize上打断点,然后让ref分别为SoftReference/WeakReference/PhantomReference,可以看到。
SoftReference/WeakReference都是不需要finalize执行就可以enqueue的。这个就否掉了luliruj所说的
-------------------------------------
这里我觉得表达不对,
finalize()和进入队列没有什么关系吧。
这2个东西,都是和对象销毁有关。
我觉得应该是对象销毁前进入队列,对象销毁是根据referent来的吧。。

也就是referent被销毁了,才进入队列

额,没注意,说晕了。
SoftReference WeakReference 在对象销毁之后,进入队列的。那finalize肯定是先执行了。

好吧。大半夜的。我都看晕。。。
说的是"不需要",我看成顺序了。。。




睡觉。。。。
22 楼 在世界的中心呼喚愛 2016-09-26  
在世界的中心呼喚愛 写道
在classB的finalize上打断点,然后让ref分别为SoftReference/WeakReference/PhantomReference,可以看到。
SoftReference/WeakReference都是不需要finalize执行就可以enqueue的。这个就否掉了luliruj所说的
-------------------------------------
这里我觉得表达不对,
finalize()和进入队列没有什么关系吧。
这2个东西,都是和对象销毁有关。
我觉得应该是对象销毁前进入队列,对象销毁是根据referent来的吧。。

也就是referent被销毁了,才进入队列

额,没注意,说晕了。
SoftReference WeakReference 在对象销毁之后,进入队列的。那finalize肯定是先执行了。
21 楼 在世界的中心呼喚愛 2016-09-26  
iteye比较少上,如果可以的话,可以发e-mail交流:christopher_y_jie@163.com
20 楼 在世界的中心呼喚愛 2016-09-26  
而PhantomReference当GC对referent的状态改变时,在把PhantomReference放入ReferenceQueue之前referent已经被GC处理到Reclaimed了,即该referent被销毁了。

---------------------------------------
楼主认为Reclaimed是什么样子?
我认为的referent的销毁是等于null

referen=null。

如果referent=null,是销毁的话。

那softreference和weakreference,是销毁之后才加入队列的。也就是执行完finalize之后,才进入队列

phantomreference则是加入队列之后,才销毁的。。

以下是我测试代码:

private static volatile boolean isRun = true;
	private static volatile ReferenceQueue<String> referenceQueue = new ReferenceQueue<String>();
	public static void main(String[] args) throws Exception {
		String abc = new String("abc");
		System.out.println(abc.getClass() + "@" + abc.hashCode());
		new Thread() {
			public void run() {
				while (isRun) {
					Object o = referenceQueue.poll();
					if (o != null) {
						try {
							Field rereferent = Reference.class.getDeclaredField("referent");
							rereferent.setAccessible(true);
							Object result = rereferent.get(o);
							//说明result是null
							System.out.println("gc will collect:" + result.getClass() + "@" + result.hashCode());
						} catch (Exception e) {
							e.printStackTrace();
						}
					}
				}
			}
		}.start();
		// 对象是弱可达的
		WeakReference<String> weak = new WeakReference<String>(abc, referenceQueue);
		System.out.println("weak=" + weak);
		// 清除强引用,触发GC
		abc = null;
		System.gc();
		//等待GC被回收
		Thread.sleep(3000);
		isRun = false;
	}
19 楼 在世界的中心呼喚愛 2016-09-26  
在classB的finalize上打断点,然后让ref分别为SoftReference/WeakReference/PhantomReference,可以看到。
SoftReference/WeakReference都是不需要finalize执行就可以enqueue的。这个就否掉了luliruj所说的
-------------------------------------
这里我觉得表达不对,
finalize()和进入队列没有什么关系吧。
这2个东西,都是和对象销毁有关。
我觉得应该是对象销毁前进入队列,对象销毁是根据referent来的吧。。

也就是referent被销毁了,才进入队列
18 楼 在世界的中心呼喚愛 2016-09-26  
public class A {  
    static A a;  
    public void finalize() {  
        a = this;  
    }  
}  
  
    ReferenceQueue queue = new ReferenceQueue();  
  
    WeakReference ref = new WeakReference(new A(), queue);  
  
    Assert.assertNotNull(ref.get());  
  
    Object obj = null;  
  
    obj = queue.poll();  
  
    Assert.assertNull(obj);  
  
    System.gc();  
  
    Thread.sleep(10000);  
  
    System.gc();  
  
    Assert.assertNull(ref.get());  
  
    obj = queue.poll();  
  
    Assert.assertNotNull(obj);  


这个代码有问题吧。。
既然对象销毁又再生,怎么会进入队列?
new A()这里放到外面实例化下:
ReferenceQueue queue = new ReferenceQueue();  
		A a=new A();
		System.out.println("a hashcode = " +a.hashCode());
	    WeakReference ref = new WeakReference(a, queue);  
	  
	    System.out.println(ref.get()); 
	    Object obj = null;  
	    obj = queue.poll();  
	    System.out.println("1=" +obj);  
	  
	    System.gc();  
	    Thread.sleep(10000);  
	    System.gc();  
	  
	    System.out.println("2=" +ref.get());  
	    obj = queue.poll();  
	    System.out.println("hashcode="+a.hashCode()+"  3=" +obj);  


17 楼 zhang_xzhi_xjtu 2014-11-11  
这个看你对回收怎么理解。
如果你认为回收表示应用不可使用该对象(对象不可达),则是否执行finalize方法,和可达性关系不大,finalize里面对象可以重生。
如果你认为回收表示对象内存被回收,则没有执行finalize方法,表示对象的内存一定还没有回收掉。

zgw06629 写道
谢谢!其实主要想问的是,没有执行finalize方法, 一定表示该对象没有被回收吗?

16 楼 zgw06629 2014-11-11  
谢谢!其实主要想问的是,没有执行finalize方法, 一定表示该对象没有被回收吗?
15 楼 zhang_xzhi_xjtu 2014-11-10  
A a = new A();
B b = new B();
b.setA(a);
b.doSomething();
b = null;
System.gc();

这个涉及到a是否可达的判断,b=null了,但是b引用的a还是可达的。
你把这个代码放在一个小方法里面,System.gc放在方法外,就很容易看到日志了。

void f(){
A a = new A();
B b = new B();
b.setA(a);
b.doSomething();
}

void g(){
f();
System.gc();
}

zgw06629 写道
你好,请教一个问题.
现遇到了这么一个问题. 弄不明白特来请教.
简单描述是这样.
A a = new A();
B b = new B();
b.setA(a);
b.doSomething();
b = null;
System.gc();
B中覆盖了finalize方法(logger.info("finalize of B")).
但是控制台中没有finalize of B的输出.

但若程序中添加了如下的语句, 便有正常的输出.

b=null;
a = null; //  添加的语句

或者这样也有输出.
//A a = new A();
B b = new B();
b.setA(new A());

不明白的是, 没有调用finalize方法,到底有没有被垃圾回收呢? 这段程序是放在一个定时任务中,所以想清楚的知道该对象有没实际被回收. 除了覆盖finalize方法外,还有其他途径可以判断该对象有没被垃圾回收吗? 谢谢!
14 楼 zgw06629 2014-11-10  
你好,请教一个问题.
现遇到了这么一个问题. 弄不明白特来请教.
简单描述是这样.
A a = new A();
B b = new B();
b.setA(a);
b.doSomething();
b = null;
System.gc();
B中覆盖了finalize方法(logger.info("finalize of B")).
但是控制台中没有finalize of B的输出.

但若程序中添加了如下的语句, 便有正常的输出.

b=null;
a = null; //  添加的语句

或者这样也有输出.
//A a = new A();
B b = new B();
b.setA(new A());

不明白的是, 没有调用finalize方法,到底有没有被垃圾回收呢? 这段程序是放在一个定时任务中,所以想清楚的知道该对象有没实际被回收. 除了覆盖finalize方法外,还有其他途径可以判断该对象有没被垃圾回收吗? 谢谢!
13 楼 zhang_xzhi_xjtu 2014-07-15  
这么老的文章都能挖出来,厉害。

trytocatch 写道
呃,我是来挖坟的。
看到评论里有很多人质疑,我也来发表下自己的观点吧,支持下博主,免得后来的读者被评论误导了。
我觉得博主还是挺严谨的,我现在用的jdk是1.7,我做了好些实验,结果都跟博主说的一致,评论中的怀疑,其实只要去测试一下就可以了的。
我补充几点吧,其它的博主已经都讲到了
1、如果没有重写finalize方法,那么gc一次就被回收了;若重写了,则第一次gc只是调用finalize方法,此时可以复活,之后再gc时,如果不可达,则回收,不再调用finalize,只会调用一次。
2、对于软引用和弱引用,入队和finalize方法的执行是没有固定顺序的,可以对ReferenceQueue的入队方法和测试对象的finalize方法分别打断点,即可测试出。
3、对于“Unlike soft and weak references, phantom references are not automatically cleared by the garbage collector as they are enqueued. An object that is reachable via phantom references will remain so until all such references are cleared or themselves become unreachable”,这里说虚引用不会被垃圾收集器自动clear,其实是这样的,虚引用根本不需要clear,因为它的get本来就只会返回null,clear的目的本来就是为了让reference的get不能再获取到引用的对象,我用反射获取了一个入队后的虚引用的referent字段,并不为null,这正如上面所说的,并没有对虚引用执行clear。

12 楼 trytocatch 2014-07-15  
呃,我是来挖坟的。
看到评论里有很多人质疑,我也来发表下自己的观点吧,支持下博主,免得后来的读者被评论误导了。
我觉得博主还是挺严谨的,我现在用的jdk是1.7,我做了好些实验,结果都跟博主说的一致,评论中的怀疑,其实只要去测试一下就可以了的。
我补充几点吧,其它的博主已经都讲到了
1、如果没有重写finalize方法,那么gc一次就被回收了;若重写了,则第一次gc只是调用finalize方法,此时可以复活,之后再gc时,如果不可达,则回收,不再调用finalize,只会调用一次。
2、对于软引用和弱引用,入队和finalize方法的执行是没有固定顺序的,可以对ReferenceQueue的入队方法和测试对象的finalize方法分别打断点,即可测试出。
3、对于“Unlike soft and weak references, phantom references are not automatically cleared by the garbage collector as they are enqueued. An object that is reachable via phantom references will remain so until all such references are cleared or themselves become unreachable”,这里说虚引用不会被垃圾收集器自动clear,其实是这样的,虚引用根本不需要clear,因为它的get本来就只会返回null,clear的目的本来就是为了让reference的get不能再获取到引用的对象,我用反射获取了一个入队后的虚引用的referent字段,并不为null,这正如上面所说的,并没有对虚引用执行clear。
11 楼 在世界的中心呼喚愛 2013-10-27  
前面看懂,后面没懂。。。
10 楼 linuxfs 2013-08-03  
linuxfs 写道
zhang_xzhi_xjtu 写道
答复我更新到主贴的最后了。

我觉得这篇文章里面对PhantomReference的理解错误了。在Oracle 的 doc里面是这样说的:soft and weak references are automatically cleared by the collector before being added to the queues with which they are registered, if any. Therefore, soft and weak references need not be registered with a queue in order to be userful, while phantome references do. An object that is reachable via phantom references will remain so until all such references are cleared or themselves become unreachable.这样GC会把PhantomReference enqueue,这样如果application能够poll到的话,可以通过调用clear清理掉referent。

我觉得其实是这样,其实GC做的工作分成是两部分,第一部分是将对象从finalizable状态到finalized状态,这只是完成了资源的释放;第二部分是reclaimed对象占用的内存。其实所有在ref中的三种reference的referent的对象的reclaimed都只有在相应reference对象的clear方法调用之后,才能进行,所以,GC只是保证weakreference、softreference的clear方法被GC自动调用,并被加到referencequeue中,但是phantomreference对象在被加入到referencequeue中之前对象就已经被GC finalied(如果定义了finalize方法的话,我所指的finalized是指调用了finalize方法)了,只是还没有进行第二步reclaimed,因为phantomreference对象的clear方法还没有被调用,所以不能进行reclaimed。
9 楼 linuxfs 2013-08-03  
zhang_xzhi_xjtu 写道
答复我更新到主贴的最后了。

我觉得这篇文章里面对PhantomReference的理解错误了。在Oracle 的 doc里面是这样说的:soft and weak references are automatically cleared by the collector before being added to the queues with which they are registered, if any. Therefore, soft and weak references need not be registered with a queue in order to be userful, while phantome references do. An object that is reachable via phantom references will remain so until all such references are cleared or themselves become unreachable.这样GC会把PhantomReference enqueue,这样如果application能够poll到的话,可以通过调用clear清理掉referent。
8 楼 zhang_xzhi_xjtu 2012-06-27  
答复我更新到主贴的最后了。
7 楼 luliruj 2012-06-25  
DLevin 写道
zhang_xzhi_xjtu 写道
引入ReferenceQueue是为了更好的监视对象回收的时机,同时可以做一些自定义的动作。
这里WeakReference和PhantomReference看似一样,但是实际还是有些不一样的。

一个对象生命周期为,正常,Finalizable ,Finalized,Reclaimed。

Enqueue之前,对WeakReference,GC保证它是Finalizable,但是有可能在执行这个对象的finalize方法时对象重生。所以监视到一个WeakReference对象enqueue并不能保证该对象已经被确定性销毁。

Enqueue之前,对PhantomReference,GC保证它是reclaimed,就是说该对象已经被认为要被确定性销毁了,没有任何机会重生了。所以我们可以在这个点做一些必须保证对象被销毁才适合做的清理工作。

JDK文档中给出关于PhantomReference的解释是:
Unlike soft and weak references, phantom references are not automatically cleared by the garbage collector as they are enqueued. An object that is reachable via phantom references will remain so until all such references are cleared or themselves become unreachable.
这样的描述,我的理解是在PhantomReference被Enqueue之后,它到目标引用还是存在的,要手动clear,虽然用get方法返回的还是null。这样是否表明目标引用还没有被回收。不知道你所知的reclaimed是指什么意思?而且在这一点上,我们如何做清理工作呢?我始终想不明白PhantomReference和finalizer机制是如何表现它的优势的,更直接一些,PhantomReference是如何使用的我也不是很了解,是否有人知道帮忙解释一些,谢谢。

我觉得LZ的对什么时候WeekReference和PhantomReference的解释是有问题的,与JDK文档也是相违背的,个人更倾向于以下的观点:
  垃圾收集器每次运行时都可以随意地释放不再是强可及的对象占用的内存。如果垃圾收集器发现了软可及对象,就会出现下列情况:
  (1)SoftReference 对象的 referent 域被设置为 null ,从而使该对象不再引用 heap 对象。
  (2)SoftReference 引用过的 heap 对象被声明为 finalizable 。
  (3)当 heap 对象的 finalize() 方法被运行而且该对象占用的内存被释放, SoftReference 对象就被添加到它的 ReferenceQueue (如果后者存在的话)。
  如果垃圾收集器发现了弱可及对象,就会出现下列情况:
  (1)WeakReference 对象的 referent 域被设置为 null ,从而使该对象不再引用 heap 对象。
  (2)WeakReference 引用过的 heap 对象被声明为 finalizable 。
  (3)当 heap 对象的 finalize() 方法被运行而且该对象占用的内存被释放时, WeakReference 对象就被添加到它的 ReferenceQueue (如果后者存在的话)。
  如果垃圾收集器发现了虚可及对象,就会出现下列情况:
  (1)PhantomReference 引用过的 heap 对象被声明为 finalizable 。
(2)与软引用和弱引用有所不同, PhantomReference 在堆对象被释放之前就被添加到它的 ReferenceQueue 。(请记住,所有的 PhantomReference 对象都必须用经过关联的 ReferenceQueue 来创建。)这使您能够在堆对象被回收之前采取行动。
以上摘自IBM上的一篇文章:http://www.ibm.com/developerworks/cn/java/j-refs/
6 楼 DLevin 2010-10-15  
zhang_xzhi_xjtu 写道
引入ReferenceQueue是为了更好的监视对象回收的时机,同时可以做一些自定义的动作。
这里WeakReference和PhantomReference看似一样,但是实际还是有些不一样的。

一个对象生命周期为,正常,Finalizable ,Finalized,Reclaimed。

Enqueue之前,对WeakReference,GC保证它是Finalizable,但是有可能在执行这个对象的finalize方法时对象重生。所以监视到一个WeakReference对象enqueue并不能保证该对象已经被确定性销毁。

Enqueue之前,对PhantomReference,GC保证它是reclaimed,就是说该对象已经被认为要被确定性销毁了,没有任何机会重生了。所以我们可以在这个点做一些必须保证对象被销毁才适合做的清理工作。

JDK文档中给出关于PhantomReference的解释是:
Unlike soft and weak references, phantom references are not automatically cleared by the garbage collector as they are enqueued. An object that is reachable via phantom references will remain so until all such references are cleared or themselves become unreachable.
这样的描述,我的理解是在PhantomReference被Enqueue之后,它到目标引用还是存在的,要手动clear,虽然用get方法返回的还是null。这样是否表明目标引用还没有被回收。不知道你所知的reclaimed是指什么意思?而且在这一点上,我们如何做清理工作呢?我始终想不明白PhantomReference和finalizer机制是如何表现它的优势的,更直接一些,PhantomReference是如何使用的我也不是很了解,是否有人知道帮忙解释一些,谢谢。
5 楼 zhang_xzhi_xjtu 2010-07-20  
可以参考我的这个博客
http://zhang-xzhi-xjtu.iteye.com/blog/484934
讲解的更详细。
对象不会被不停地被gc(这个的gc指的是执行finalize方法)。

hfhwan 写道
zhang_xzhi_xjtu 写道
引入ReferenceQueue是为了更好的监视对象回收的时机,同时可以做一些自定义的动作。
这里WeakReference和PhantomReference看似一样,但是实际还是有些不一样的。

一个对象生命周期为,正常,Finalizable ,Finalized,Reclaimed。

Enqueue之前,对WeakReference,GC保证它是Finalizable,但是有可能在执行这个对象的finalize方法时对象重生。所以监视到一个WeakReference对象enqueue并不能保证该对象已经被确定性销毁。

Enqueue之前,对PhantomReference,GC保证它是reclaimed,就是说该对象已经被认为要被确定性销毁了,没有任何机会重生了。所以我们可以在这个点做一些必须保证对象被销毁才适合做的清理工作。


我想问一下,gc是如何保证WeakReference是Finalizable,PhantomReference是reclaimed。

我看到有的文章说WeakReference是在fnalize执行之后,对象被销毁之后放入referenceQueue的。
而PhantomReference是在finalize执行之后,对象被销毁之前放入refrenceQueue的。

如果都是在finalize执行之后,那如你文章中说到的,在finalize函数中使其复活后,这个对象难道还是会被标识成Finalizable,从而被放到referenceQueue中。
这样的话不是会引起该对象不停地被gc?

相关推荐

Global site tag (gtag.js) - Google Analytics