现在是2010/10/03 日凌晨4:50,可能是"WeakReference 与 PhantomReference 加入ReferenceQueue时机的疑惑 "一文中提出的问题没有解决一直睡不着吧:P 突然想到去check一下这个项目用的JRE,发现自己太粗心了,虽然Eclipse启动是用的SUN JRE1.6,但这个项目配置的却是系统上装的另一个IBM JRE1.5。所以在"WeakReference 与 PhantomReference 加入ReferenceQueue时机的疑惑 "一文的评论中我所贴的实际上是IBM JRE1.5的原码,所以我便开始怀疑是不是IBM的实现有问题? 于是将项目的JRE改过来后重新执行程序,结果就成了:
Phantom Reference refers to null in finalize(). Weak Reference refers to null in finalize(). java.lang.ref.WeakReference is added in the queue out of finalize().
首先,WeakReference终于是被放入到Queue中去了,但看样子像是在finalize()之后,其次SoftReference引用的Object索性就不回收了(这应该是符合定义的,softly reachable的对象应该还会再逗留一段时间)。但PhantomReference 还是没有被加入到Queue中去。于是去翻阅了一下Java Specification 3.0,没找到相关的内容,所以就去查了一下JDK1.6的Java Doc。首先它对Reachability的定义如下:
Going from strongest to weakest, the different levels of reachability reflect the life cycle of an object. They are operationally defined as follows:
-- 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.
从中可见:WeakReference是在finalize()之前被clear的,这点IBM和SUN实现都一样。但还是没提加入Queue的时机。PhantomReference在finalize()之后还是会指向对象(依然没有加入到Queue中),这点也在两个JRE中印证了。
于是我们来看Notification 这段的描述:
这里就明确了,WeakReference应该在Object weakly reachable的时候被加入到Queue中,也就是说应该在finalize()之前。可见IBM JRE执行程序时不退出循环肯定是实现有问题的。SUN JRE虽然正常输出了,但为什么结果却显示不正确呢。于是我想到可能是System.gc()的问题,它并不保证finalize(),而我又在后面的循环中从Queue中取出了Reference,这有可能发生在finalize()之前。所以我将程序改进了一下:
import java.lang.ref.PhantomReference; import java.lang.ref.Reference; import java.lang.ref.ReferenceQueue; import java.lang.ref.SoftReference; import java.lang.ref.WeakReference; class TestObj { // 定义三种Reference用于保存指向本对象的Reference,从而在finalize中检验他们的get()值 private SoftReference sr = null; private WeakReference wr = null; private PhantomReference pr = null; // 定义ReferenceQueue用于在finalize中检验指向本对象的reference是否被加入了这个Queue private ReferenceQueue rq = null; // 有多少个对象被finalize()了 static private int finalizeCount = 0; synchronized public static int getFinalizeCount() { return finalizeCount; } synchronized public static void increaseFinalizeCount() { finalizeCount++; } public void setQueue(ReferenceQueue referenceQueue) { rq = referenceQueue; } public void setSr(SoftReference sr) { this.sr = sr; } public void setWr(WeakReference wr) { this.wr = wr; } public void setPr(PhantomReference pr) { this.pr = pr; } public void finalize() { // 检验指向本对象的Reference的get()值 if (null != sr) System.out.println("Soft Reference refers to " + sr.get() + " in finalize()."); if (null != wr) System.out.println("Weak Reference refers to " + wr.get() + " in finalize()."); if (null != pr) System.out.println("Phantom Reference refers to " + pr.get() + " in finalize()."); // 验证是不是在对象finalize()的时候他的soft reference已经被加入至Queue中 Reference r = rq.poll(); while (null != r) { System.out.println(r.getClass().getName() + " is added in the queue in finalize()."); r = rq.poll(); } increaseFinalizeCount(); } } public class TestReference { public static void main(String[] args) { { // 用于传给三种Reference的强引用 TestObj softObj = new TestObj(); TestObj weakObj = new TestObj(); TestObj phantomObj = new TestObj(); // 用于放三种Reference的三个Queue ReferenceQueue softrq = new ReferenceQueue(); ReferenceQueue weakrq = new ReferenceQueue(); ReferenceQueue phantomrq = new ReferenceQueue(); // 三种Reference指向三个不同的对象 SoftReference sr = new SoftReference(softObj, softrq); WeakReference wr = new WeakReference(weakObj, weakrq); PhantomReference pr = new PhantomReference(phantomObj, phantomrq); // 将Reference传回给对应的对象 softObj.setSr(sr); weakObj.setWr(wr); phantomObj.setPr(pr); // 将ReferenceQueue传回给对象 softObj.setQueue(softrq); weakObj.setQueue(weakrq); phantomObj.setQueue(phantomrq); // 删除强引用 softObj = null; weakObj = null; phantomObj = null; // 确保三个对象都被finalize() while (3 != TestObj.getFinalizeCount()) { System.gc(); } Reference r = phantomrq.poll(); if (null != r) System.out.println(r.getClass().getName() + " is added in the queue out of finalize()."); } } }
于是就看到了如下的结果:
Phantom Reference refers to null in finalize(). Weak Reference refers to null in finalize(). java.lang.ref.WeakReference is added in the queue in finalize(). Soft Reference refers to null in finalize(). java.lang.ref.SoftReference is added in the queue in finalize(). java.lang.ref.PhantomReference is added out of the queue in finalize().
前三行结果很快就出来了,后三行结果过了好一会儿才出来,程序正常结束。可见只要足够多次System.gc(),SoftReference引用的对象还是会被finalize()的,而PhantomReference确实是在object被回收的时候被加入了Queue。从上面的结果我们可以看出SoftReference和WeakReference确实是在finalize()之前就被clear并被加入到Queue中了。
于是又改回到IBM JRE1.5重新执行一下这个程序,结果就变成了:
Phantom Reference refers to null in finalize(). Weak Reference refers to null in finalize(). Soft Reference refers to null in finalize().
可见IBM的实现绝对是有问题的。ReferenceQueue根本没起到作用。
但为了让程序逻辑更清楚,有更清晰的输出,我又对上面这个程序进行了重写如下:
import java.lang.ref.PhantomReference; import java.lang.ref.Reference; import java.lang.ref.ReferenceQueue; import java.lang.ref.SoftReference; import java.lang.ref.WeakReference; //这个类负责check Reference什么时候被clear,以及什么时候被加入到Queue中 class CheckReference implements Runnable { // 要被check 的Reference private Reference r = null; // 用于check 上面的reference何时加入到Queue中的那个ReferenceQueue private ReferenceQueue rq = null; CheckReference(Reference r, ReferenceQueue rq) { this.r = r; this.rq = rq; } public void run() { //Check Reference何时被clear,PhantomReference的get()始终返回null,它被clear与加入Queue等价 if (!(r instanceof PhantomReference)) { while (r.get() != null){ // 调用GC,确保对象被finalize() System.gc(); } System.out.println(r.getClass().getName() + " is cleared."); } // check Reference何时被加入到了Queue中 Reference r = rq.poll(); while (null == r) { // 调用GC,确保对象被reclaim System.gc(); r = rq.poll(); } System.out.println(r.getClass().getName() + " is added in the queue."); } } class TestObj { private String name = "Anonymous"; TestObj(String name) { this.name = name; } public void finalize() { System.out.println("TestObj " + this.name + " is finalized."); } } public class TestReference { public static void main(String[] args) { { // 用于传给三种Reference的强引用 TestObj softObj = new TestObj("Soft"); TestObj weakObj = new TestObj("Weak"); TestObj phantomObj = new TestObj("Phantom"); // 用于放三种Reference的三个Queue ReferenceQueue softrq = new ReferenceQueue(); ReferenceQueue weakrq = new ReferenceQueue(); ReferenceQueue phantomrq = new ReferenceQueue(); // 三种Reference指向三个不同的对象 SoftReference sr = new SoftReference(softObj, softrq); WeakReference wr = new WeakReference(weakObj, weakrq); PhantomReference pr = new PhantomReference(phantomObj, phantomrq); // 创建对应三种Reference的check task CheckReference softCheck = new CheckReference(sr, softrq); CheckReference weakCheck = new CheckReference(wr, weakrq); CheckReference phantomCheck = new CheckReference(pr, phantomrq); // 创建对应的三个thread Thread softThread = new Thread(softCheck); Thread weakThread = new Thread(weakCheck); Thread phantomThread = new Thread(phantomCheck); // 把他们的优先级设高点增加他们被调用的机会,从而当一有变化时他们就能输出 softThread.setPriority(Thread.MAX_PRIORITY); weakThread.setPriority(Thread.MAX_PRIORITY); phantomThread.setPriority(Thread.MAX_PRIORITY); // 启动这三个线程 softThread.start(); weakThread.start(); phantomThread.start(); // 删除强引用 softObj = null; weakObj = null; phantomObj = null; } } }
发现这样做结果就变成了:
TestObj Weak is finalized. TestObj Phantom is finalized. java.lang.ref.WeakReference is cleared. java.lang.ref.WeakReference is added in the queue. java.lang.ref.PhantomReference is added in the queue.
并且程序始终结束不了。和之前的程序比较了半天,又想了半天,觉得应该是我一直调用SoftReference()的get()方法的问题,于是又将程序改写如下:
import java.lang.ref.PhantomReference; import java.lang.ref.Reference; import java.lang.ref.ReferenceQueue; import java.lang.ref.SoftReference; import java.lang.ref.WeakReference; import java.util.concurrent.TimeUnit; //这个类负责check Reference什么时候被clear,以及什么时候被加入到Queue中 class CheckReference implements Runnable { // 要被check 的Reference private Reference r = null; // 用于check 上面的reference何时加入到Queue中的那个ReferenceQueue private ReferenceQueue rq = null; CheckReference(Reference r, ReferenceQueue rq) { this.r = r; this.rq = rq; } public void run() { // Check // Reference何时被clear,PhantomReference的get()始终返回null,它被clear与加入Queue等价 if (!(r instanceof PhantomReference)) { if (r instanceof WeakReference) { while (r.get() != null) { // 调用GC,确保对象被finalize() System.gc(); } } else { // 不调用SoftReference的get() while (3 != TestObj.getFinalizeCount()) // 调用GC,确保对象被finalize() System.gc(); } System.out.println(r.getClass().getName() + " is cleared."); } // check Reference何时被加入到了Queue中 Reference r = rq.poll(); while (null == r) { // 调用GC,确保对象被reclaim System.gc(); r = rq.poll(); } System.out.println(r.getClass().getName() + " is added in the queue."); // 睡一会儿,以防程序马上终止导致finalize()还没执行完。 try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } enum ReferenceLevel { STRONG, SOFT, WEAK, PHANTOM } class TestObj { private String name = "Anonymous"; // 有多少个对象被finalize()了 static private int finalizeCount = 0; // 定义指向本对象的引用级别 private ReferenceLevel rf = ReferenceLevel.STRONG; public void setRf(ReferenceLevel rf) { this.rf = rf; } synchronized public static int getFinalizeCount() { return finalizeCount; } synchronized public static void increaseFinalizeCount() { finalizeCount++; } TestObj(String name) { this.name = name; } public void finalize() { increaseFinalizeCount(); // 如果是SoftReference, 睡一会儿,让Check Task有机会被调度 if (ReferenceLevel.SOFT.equals(this.rf)) { try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println("TestObj " + this.name + " is finalized."); } } public class TestReference { public static void main(String[] args) { { // 用于传给三种Reference的强引用 TestObj softObj = new TestObj("Soft"); TestObj weakObj = new TestObj("Weak"); TestObj phantomObj = new TestObj("Phantom"); // 用于放三种Reference的三个Queue ReferenceQueue softrq = new ReferenceQueue(); ReferenceQueue weakrq = new ReferenceQueue(); ReferenceQueue phantomrq = new ReferenceQueue(); // 三种Reference指向三个不同的对象 SoftReference sr = new SoftReference(softObj, softrq); WeakReference wr = new WeakReference(weakObj, weakrq); PhantomReference pr = new PhantomReference(phantomObj, phantomrq); // 设置引用级别 softObj.setRf(ReferenceLevel.SOFT); weakObj.setRf(ReferenceLevel.WEAK); phantomObj.setRf(ReferenceLevel.PHANTOM); // 创建对应三种Reference的check task CheckReference softCheck = new CheckReference(sr, softrq); CheckReference weakCheck = new CheckReference(wr, weakrq); CheckReference phantomCheck = new CheckReference(pr, phantomrq); // 创建对应的三个thread Thread softThread = new Thread(softCheck); Thread weakThread = new Thread(weakCheck); Thread phantomThread = new Thread(phantomCheck); // 把他们的优先级设高点增加他们被调用的机会,从而当一有变化时他们就能输出 softThread.setPriority(Thread.MAX_PRIORITY); weakThread.setPriority(Thread.MAX_PRIORITY); phantomThread.setPriority(Thread.MAX_PRIORITY); // 启动这三个线程 softThread.start(); weakThread.start(); phantomThread.start(); // 删除强引用 softObj = null; weakObj = null; phantomObj = null; } } }
结果就变成了:
java.lang.ref.WeakReference is cleared. java.lang.ref.WeakReference is added in the queue. TestObj Phantom is finalized. TestObj Weak is finalized. java.lang.ref.PhantomReference is added in the queue. java.lang.ref.SoftReference is cleared. java.lang.ref.SoftReference is added in the queue. TestObj Soft is finalized.
终于达到想要的结果了,执行了几次结果都是相同的。
总结:
1) SoftReference在当object softly reachable 的时候并不一定会被clear 和加入Reference Queue中,这主要由当前内存使用是否吃紧与SoftReference是否还在经常被使用相关(get()方法是否还在经常被调用)。但clear和被加入Reference Queue中的时机都在finalize()之前。
2) WeakReference在当object weakly reachable的时候就会被clear 和加入Reference Queue中,并且都在finalize()之前。
3) PhantomReference只有在Object真正被回收的时候才被加入到Reference Queue中,是在 finalize()被调用之后的下次GC中。
4) 三种Reference都是在GC的时候才被决定是否clear 和加入Queue中的。
相关推荐
MySQL 5.6 Reference Manual MySQL 5.6 Reference Manual MySQL 5.6 Reference Manual MySQL 5.6 Reference Manual MySQL 5.6 Reference Manual MySQL 5.6 Reference Manual MySQL 5.6 Reference Manual ...
Welcome to the Command Reference.This reference contains a complete dictionary of detailed command descriptions, arranged in alphabetical order. It is the definitive resource for correct command ...
OpenGL Reference Manual OpenGL Reference Manual
SQL_Reference.rar 是一个关于SQL语言的参考资料,特别针对SQL Server这款流行的数据库管理系统。这个压缩包包含了一个名为"SQL_Reference.chm"的帮助文件,通常这种文件格式是Microsoft编写的帮助文档,方便用户...
"Find Reference 2 插件包"是一款专为大型项目设计的高效工具,它在Unity游戏开发环境中扮演着至关重要的角色。在处理复杂的项目时,往往会出现资源冗余,这不仅占用了大量的存储空间,还可能导致性能下降。该插件...
标题中的"PDF Reference 1.7_1.7_reference_信息_pdf1.7格式_pdf_"表明我们将探讨的是关于PDF格式的1.7版本的官方参考指南。 PDF 1.7是PDF格式的一个重要里程碑,它被ISO标准化为ISO 32000-1,是目前许多PDF文档的...
《Android M 原生RIL库:reference-ril详解》 在Android系统中,Radio Interface Layer(RIL)扮演着至关重要的角色,它是操作系统与无线调制解调器之间的桥梁,负责处理通信相关的任务,如语音通话、数据传输、...
Qt Reference Documentation
OracleDatabase SQL Reference OracleDatabase SQL Reference OracleDatabase SQL Reference
新买的Find Reference2插件,经测试可用 https://assetstore.unity.com/packages/tools/find-reference-2-59092 FR2 helps instantly clean up asset references for your projects Email | Documentation | ...
Unity资源引用查找超级利器,非常好用,推荐给大家。 https://assetstore.unity.com/packages/tools/find-reference-2-59092
Solaris Express Reference Manual CollectionSolaris Express Reference Manual CollectionSolaris Express Reference Manual CollectionSolaris Express Reference Manual CollectionSolaris Express Reference ...
The CSS Quick Syntax Reference is a 150-page syntax reference to the Cascading Style Sheet specification and style sheet language. It presents the essentials of CSS in a well-organized format that can...
1.PDF Reference 1.7(PDF Reference sixth edition Adobe Portable Document Format Version 1.7 November 2006) 中文自翻译版 2.PDF Reference 1.6 中文版 3.PDFPatcher 检测PDF.0.5.0.2975
ZK 5.0.5 Component Reference ZK 5.0.5 Configuration Reference ZK 5.0.5 Style Guide ZK 5.0.5 ZUML Reference
Volume 2, Xlib Reference Manual, is a complete programmer's reference for Xlib . Covers X11 Release 4 and Release 5. Contents Include: Reference pages for Xlib functions Reference pages for event ...
在Java编程语言中,`Reference`类是一个非常特殊且重要的概念,它超出了常规的引用类型,如`Object`或数组引用。`Reference`类及其子类主要用于处理对象的软引用、弱引用和虚引用,这些引用类型在内存管理,特别是...
《UVM Class Reference Manual 1.2》是验证方法学(Unified Verification Methodology,简称UVM)中的核心文档,它详细介绍了UVM 1.2版本的类库及其使用方法。UVM是一种基于SystemVerilog的工业标准,用于硬件验证,...
UIKit Framework Reference文档详细介绍了UIKit框架内的所有类和函数,对于iOS和tvOS开发者来说是核心参考资料之一。 在文档中,我们可以看到UIKit框架中包含了许多不同的类和模块,每个都有其特定的功能和用途。...
MySQL 5.1 Reference Manual