Posted on 2008-11-18 14:50
英雄 阅读(644)
评论(0) 编辑 收藏
GC,Reference,Finalize,Dispose
Java提供了垃圾对象自动回收(GC)机制,该机制对堆heap里的对象就其被引用情况进行跟踪判断,对合适对象进行自动回收释放内存。按java规范,对象分如下引用情况:
- An object is strongly reachable if it can be reached by some thread without traversing any reference objects. A newly-created object is strongly reachable by the thread that created it.
- An object is softly reachable if it is not strongly reachable but can be reached by traversing a soft reference.
- An object is weakly reachable if it is neither strongly nor softly reachable but can be reached by traversing a weak reference. When the weak references to a weakly-reachable object are cleared, the object becomes eligible for finalization.
- An object is phantom reachable if it is neither strongly, softly, nor weakly reachable, it has been finalized, and some phantom reference refers to it.
- Finally, an object is unreachable, and therefore eligible for reclamation, when it is not reachable in any of the above ways.
根据引用情况不同,GC处理过程也不同。
当一个对象实例不被强引用strongly reachable时,GC的某次运行就有可能扫描到该对象。这时GC会检查该对象是否softreference reachable,如果是,则尽可能放它一马,一笑而过,但如果放过去就会引起out of memory error,则就要处理该对象。处理过程首先要检查是否实现了finalize方法的对象,如果是则标记finalizable,并导致Finalizer系统线程(setDaemon(true),Thread.MAX_PRIORITY - 2)在后续巡检中对此对象调用finalize方法。执行完finalize方法后如果在此后某次的GC运行中再次被发现softreference reachable,则此时导致clear softreference,并释放内存,最后归到softreference-queue中。
如果本对象没有实现finalize方法,则GC省去其对应处理环节。
此过程同样适用于weakreference reachable,所不同的是GC将不会尽可能放过该对象。
对于PhantomReference reachable,情况不同之处在于PhantomReference reachable定义在finalize已经执行结束后的GC的某次运行时,这个时候,它不去PhantomReference clear,也不释放内存,而是直接归到PhantomReference-queue中,此后需要应用程序自行clear才能释放,这也是PhantomReference一定要求queue的原因。
基于此GC机制,对“实现当不使用某对象时释放其附属资源”这样的需求,java提供两种实现方式。
Finalize方式就是指Object class的那个Finalize方法,可以被任何class进行override来实现释放对应附属资源。由于历史原因,执行该finalize方法的Finalizer线程保留为一个低优先级线程,因此导致获得巡检机会从而进行资源释放将不是十分及时;另外,java规范允许在finalize方法中再次使该对象恢复强引用,因而GC总是在执行finalize后的再次运行中才考虑释放该对象的内存,如果该对象本身占用内存比较大,这样同样导致整体资源释放将不是十分及时。最后,如果一个class实现了finalize方法,不仅如上所述释放其实例时比较占用机器资源,建立实例的过程也将因要通过必须的finalize方法检测及底层register而占用额外的资源。为此,java提供了Dispose模式可以用来替代finalize模式。
Dispose方式的实现就不在class里实现finalize方法了。在jre中,已经为java2D的支持实现了一个Disposer用来帮助释放对象附用的图形资源,下面以此为例说明Dispose方式。
sun.java2d.Dispose随着类加载初始化构建一个Java2D Disposer线程(setDaemon(true),Thread.MAX_PRIORITY),同时该类对外提供public static addRecord(Object target, DisposerRecord rec)用来建立对指定target的weakreference或 PhantomReference(系统属性sun.java2d.reftype指定)监测。当指定对象target失去强引用而成为weakreference或PhantomReference reachable时,会被GC及时置入reference-queue中,同时被高优先级的本Java2D Disposer线程及时监测到,然后本线程会去从reference-queue中拿到此reference,并通过内部映射表hashtable找到当初addRecord时传入的DisposerRecord,并调用其dispose()。所以在该dispose方法中实现释放附属资源的逻辑就可以了。