2009年1月18日星期日

用 PhantomReference 避免OutOfMemory

PhantomReference比Weak/Soft的引用强度都要低,PhantomReference.get()总是返回null,为什么呢?

其实PhantomReference而要是不可缺少的重要引用类型, 我们知道WeakReference在finalize方法调用或被gc清理之前会进入ReferenceQueue, 这时候没有任何strong reference引用对象,然后可以通过ReferenceQueue去做收尾工作。但是在finalize()方法,或者重新给这个对象一个引用使它reachable(从某个活动线程调用栈,或者静态变量可达), 使这个对象延长生命周期暂时不会被gc清理。而PhantomReference只会在对象被从内存中清除后才会进入队列,get()总是返回null (ReferenceQueue通知一个PhantomReference的时候,既然内存都已经物理释放了,当然也无法给你一个对象,所以get总是返回null也有这个原因), 所以你没有办法重新使这个对象再次reachable。PhantomReference只是提供了一种方式让你跟踪一个曾经产生过的对象,由此让你知道这个对象到底有没有被物理的清除。

比如当你的applet程序需要处理一个非常大的图片的时候,你可能希望图片处理结束并且内存被释放之后再处理下一个图片。如果用PhantomReference来引用上一个图片对象,当ReferenceQueue通知你的时候,你就可以知道上一个内存对象已经被物理清除,你可以继续下一个大内存对象的处理了。这样就可以避免由于GC线程优先级低导致上一个大内存对象还没有释放下一个大内存对象又被创建,让OutOfMemoryError出现的概率低一些。

还有一个好处,就是PhantomReference比用finalize方法好的多,因为VM对finalize的处理不如PhantomReference简单可靠,只不过是你要写的代码稍微多一点点而已。


Ethan有一个更完整的对四种reference的解释 http://weblogs.java.net/blog/enicholas/archive/2006/05/understanding_w.html

没有评论: