下载本文示例代码
当在 Java 2 平台中首次引入 java.lang.ref 包(其中包含 SoftReference、WeakReference 和 PhantomReference 类)时,它的实用性显然被过分夸大了。它包含的类可能是有用的,但这些类具有的某些局限性会使它们显得不是很有吸引力,而且其应用程序也将特别局限于解决一类特定的问题。
垃圾收集概述 引用类的主要功能就是能够引用仍可以被垃圾收集器回收的对象。在引入引用类之前,我们只能使用强引用(strong reference)。举例来说,下面一行代码显示的就是强引用 obj:
Object obj = new Object();
obj 这个引用将引用堆中存储的一个对象。只要 obj 引用还存在,垃圾收集器就永远不会释放用来容纳该对象的存储空间。
当 obj 超出范围或被显式地指定为 null 时,垃圾收集器就认为没有对这个对象的其它引用,也就可以收集它了。然而您还需要注意一个重要的细节:仅凭对象可以被收集并不意味着垃圾收集器的一次指定运行就能够回收它。由于各种垃圾收集算法有所不同,某些算法会更频繁地分析生存期较短的对象,而不是较老、生存期较长的对象。因此,一个可供收集的对象可能永远也不会被回收。如果程序在垃圾收集器释放对象之前结束,这种情况就可能会出现。因此,概括地说,您永远无法保证可供收集的对象总是会被垃圾收集器收集。
这些信息对于您分析引用类是很重要的。由于垃圾收集有着特定的性质,所以引用类实际上可能没有您原来想像的那么有用,尽管如此,它们对于特定问题来说还是很有用的类。软引用(soft reference)、弱引用(weak reference)和虚引用(phantom reference)对象提供了三种不同的方式来在不妨碍收集的情况下引用堆对象。每种引用对象都有不同的行为,而且它们与垃圾收集器之间的交互也有所不同。此外,这几个新的引用类都表现出比典型的强引用“更弱”的引用形式。而且,内存中的一个对象可以被多个引用(可以是强引用、软引用、弱引用或虚引用)引用。在进一步往下讨论之前,让我们来看看一些术语:
强可及对象(strongly reachable):可以通过强引用访问的对象。
软可及对象(softly reachable):不是强可及对象,并且能够通过软引用访问的对象。
弱可及对象(weakly reachable):不是强可及对象也不是软可及对象,并且能够通过弱引用访问的对象。
虚可及对象(phantomly reachable):不是强可及对象、软可及对象,也不是弱可及对象,已经结束的,可以通过虚引用访问的对象。
清除:将引用对象的 referent 域设置为 null,并将引用类在堆中引用的对象声明为可结束的。
SoftReference 类 SoftReference 类的一个典型用途就是用于内存敏感的高速缓存。SoftReference 的原理是:在保持对对象的引用时保证在 JVM 报告内存不足情况之前将清除所有的软引用。关键之处在于,垃圾收集器在运行时可能会(也可能不会)释放软可及对象。对象是否被释放取决于垃圾收集器的算法以及垃圾收集器运行时可用的内存数量。
WeakReference 类 WeakReference 类的一个典型用途就是规范化映射(canonicalized mapping)。另外,对于那些生存期相对较长而且重新创建的开销也不高的对象来说,弱引用也比较有用。关键之处在于,垃圾收集器运行时如果碰到了弱可及对象,将释放 WeakReference 引用的对象。然而,请注意,垃圾收集器可能要运行多次才能找到并释放弱可及对象。
PhantomReference 类 PhantomReference 类只能用于跟踪对被引用对象即将进行的收集。同样,它还能用于执行 pre-mortem 清除操作。PhantomReference 必须与 ReferenceQueue 类一起使用。需要 ReferenceQueue 是因为它能够充当通知机制。当垃圾收集器确定了某个对象是虚可及对象时,PhantomReference 对象就被放在它的 ReferenceQueue 上。将 PhantomReference 对象放在 ReferenceQueue 上也就是一个通知,表明 PhantomReference 对象引用的对象已经结束,可供收集了。这使您能够刚好在对象占用的内存被回收之前采取行动。
垃圾收集器和引用交互 垃圾收集器每次运行时都可以随意地释放不再是强可及的对象占用的内存。如果垃圾收集器发现了软可及对象,就会出现下列情况:
SoftReference 对象的 referent 域被设置为 null,从而使该对象不再引用 heap 对象。
SoftReference 引用过的 heap 对象被声明为 finalizable。
当 heap 对象的 finalize() 方法被运行而且该对象占用的内存被释放,SoftReference 对象就被添加到它的 ReferenceQueue(如果后者存在的话)。
如果垃圾收集器发现了弱可及对象,就会出现下列情况:
WeakReference 对象的 referent 域被设置为 null,从而使该对象不再引用 heap 对象。
WeakReference 引用过的 heap 对象被声明为 finalizable。
当 heap 对象的 finalize() 方法被运行而且该对象占用的内存被释放时,WeakReference 对象就被添加到它的 ReferenceQueue(如果后者存在的话)。
如果垃圾收集器发现了虚可及对象,就会出现下列情况:
PhantomReference 引用过的 heap 对象被声明为 finalizable。
与软引用和弱引用有所不同,PhantomReference 在堆对象被释放之前就被添加到它的 ReferenceQueue。(请记住,所有的 PhantomReference 对象都必须用经过关联的 ReferenceQueue 来创建。)这使您能够在堆对象被回收之前采取行动。
请考虑清单 1 中的代码。图 1 说明了这段代码的执行情况。
清单 1. 使用 WeakReference 及 ReferenceQueue 的示例代码
//Create a strong reference to an objectMyObject obj = new MyObject(); //1//Create a reference queueReferenceQueue rq = new ReferenceQueue(); //2 //Create a weakReference to obj and associate our reference queueWeakReference wr = new WeakReference(obj, rq); //3
图 1. 执行了清单 1 中行 //1、//2 和 //3 的代码之后的对象布局
图 1 显示了每行代码执行后各对象的状态。行 //1 创建 MyObject 对象,而行 //2 则创建 ReferenceQueue 对象。行 //3 创建引用其引用对象 MyObject 的 WeakReference 对象,还创建它的 ReferenceQueue。请注意,每个对象引用(obj、rq 及 wr)都是强引用。要利用这些引用类,您必须取消对 MyObject 对象的强引用,方法是将 obj 设置为 null。前面说过,如果不这样做,对象 MyObject 永远都不会被回收,引用类的任何优点都会被削弱。
每个引用类都有一个 get() 方法,而 ReferenceQueue 类有一个 poll() 方法。get() 方法返回对被引用对象的引用。在 PhantomReference 上调用 get() 总是会返回 null。这是因为 PhantomReference 只用于跟踪收集。poll() 方法返回已被添加到队列中的引用对象,如果队列中没有任何对象,它就返回 null。因此,执行清单 1 之后再调用 get() 和 poll() 的结果可能是:
wr.get(); //returns reference to MyObjectrq.poll(); //returns null
现在我们假定垃圾收集器开始运行。由于 MyObject 对象没有被释放,所以 get() 和 poll() 方法将返回同样的值;obj 仍然保持对该对象进行强引用。实际上,对象布局还是没有改变,和图 1 所示的差不多。然而,请考虑下面的代码:
obj = null;System.gc(); //run the collector
在这段代码执行后,对象布局就如图 2 所示:
图 2. obj = null; 和垃圾收集器运行后的对象布局
现在,调用 get() 和 poll() 将产生与前面不同的结果:
wr.get(); //returns nullrq.poll(); //returns a reference to the WeakReference object
这种情况表明,MyObject 对象(对它的引用原来是由 WeakReference 对象进行的)不再可用。这意味着垃圾收集器释放了 MyObject 占用的内存,从而使 WeakReference 对象可以被放在它的 ReferenceQueue 上。这样,您就可以知道当 WeakReference 或 SoftReference 类的 get() 方法返回 null 时,就有一个对象被声明为 finalizable,而且可能(不过不一定)被收集。只有当 heap 对象完全结束而且其内存被回收后,WeakReference 或 SoftReference 才会被放到与其关联的 ReferenceQueue 上。清单 2 显示了一个完整的可运行程序,它展示了这些原理中的一部分。这段代码本身就颇具说明性,它含有很多注释和打印语句,可以帮助您理解。
清单 2. 展示引用类原理的完整程序
import java.lang.ref.*;class MyObject{ protected void finalize() throws Throwable { System.out.println("In finalize method for this object: " this); }}class ReferenceUsage{ public static void main(String args[]) { hold(); release(); } public static void hold() { System.out.println("Example of incorrectly holding a strong " "reference"); //Create an object MyObject obj = new MyObject(); System.out.println("object is " obj); //Create a reference queue ReferenceQueue rq = new ReferenceQueue(); //Create a weakReference to obj and associate our reference queue WeakReference wr = new WeakReference(obj, rq); System.out.println("The weak reference is " wr); //Check to see if it's on the ref queue yet System.out.println("Polling the reference queue returns " rq.poll()); System.out.println("Getting the referent from the " "weak reference returns " wr.get()); System.out.println("Calling GC"); System.gc(); System.out.println("Polling the reference queue returns " rq.poll()); System.out.println("Getting the referent from the " "weak reference returns " wr.get()); } public static void release() { System.out.println(""); System.out.println("Example of correctly releasing a strong " "reference"); //Create an object MyObject obj = new MyObject(); System.out.println("object is " obj); //Create a reference queue ReferenceQueue rq = new ReferenceQueue(); //Create a weakReference to obj and associate our reference queue WeakReference wr = new WeakReference(obj, rq); System.out.println("The weak reference is " wr); //Check to see if it's on the ref queue yet System.out.println("Polling the reference queue returns " rq.poll()); System.out.println("Getting the referent from the " "weak reference returns " wr.get()); System.out.println("Set the obj reference to null and call GC"); obj = null; System.gc(); System.out.println("Polling the reference queue returns " rq.poll()); System.out.println("Getting the referent from the " "weak reference returns " wr.get()); }}共2页。 1 2 :
当在 Java 2 平台中首次引入 java.lang.ref 包(其中包含 SoftReference、WeakReference 和 PhantomReference 类)时,它的实用性显然被过分夸大了。它包含的类可能是有用的,但这些类具有的某些局限性会使它们显得不是很有吸引力,而且其应用程序也将特别局限于解决一类特定的问题。
垃圾收集概述 引用类的主要功能就是能够引用仍可以被垃圾收集器回收的对象。在引入引用类之前,我们只能使用强引用(strong reference)。举例来说,下面一行代码显示的就是强引用 obj:
Object obj = new Object();
obj 这个引用将引用堆中存储的一个对象。只要 obj 引用还存在,垃圾收集器就永远不会释放用来容纳该对象的存储空间。
当 obj 超出范围或被显式地指定为 null 时,垃圾收集器就认为没有对这个对象的其它引用,也就可以收集它了。然而您还需要注意一个重要的细节:仅凭对象可以被收集并不意味着垃圾收集器的一次指定运行就能够回收它。由于各种垃圾收集算法有所不同,某些算法会更频繁地分析生存期较短的对象,而不是较老、生存期较长的对象。因此,一个可供收集的对象可能永远也不会被回收。如果程序在垃圾收集器释放对象之前结束,这种情况就可能会出现。因此,概括地说,您永远无法保证可供收集的对象总是会被垃圾收集器收集。
这些信息对于您分析引用类是很重要的。由于垃圾收集有着特定的性质,所以引用类实际上可能没有您原来想像的那么有用,尽管如此,它们对于特定问题来说还是很有用的类。软引用(soft reference)、弱引用(weak reference)和虚引用(phantom reference)对象提供了三种不同的方式来在不妨碍收集的情况下引用堆对象。每种引用对象都有不同的行为,而且它们与垃圾收集器之间的交互也有所不同。此外,这几个新的引用类都表现出比典型的强引用“更弱”的引用形式。而且,内存中的一个对象可以被多个引用(可以是强引用、软引用、弱引用或虚引用)引用。在进一步往下讨论之前,让我们来看看一些术语:
强可及对象(strongly reachable):可以通过强引用访问的对象。
软可及对象(softly reachable):不是强可及对象,并且能够通过软引用访问的对象。
弱可及对象(weakly reachable):不是强可及对象也不是软可及对象,并且能够通过弱引用访问的对象。
虚可及对象(phantomly reachable):不是强可及对象、软可及对象,也不是弱可及对象,已经结束的,可以通过虚引用访问的对象。
清除:将引用对象的 referent 域设置为 null,并将引用类在堆中引用的对象声明为可结束的。
SoftReference 类 SoftReference 类的一个典型用途就是用于内存敏感的高速缓存。SoftReference 的原理是:在保持对对象的引用时保证在 JVM 报告内存不足情况之前将清除所有的软引用。关键之处在于,垃圾收集器在运行时可能会(也可能不会)释放软可及对象。对象是否被释放取决于垃圾收集器的算法以及垃圾收集器运行时可用的内存数量。
WeakReference 类 WeakReference 类的一个典型用途就是规范化映射(canonicalized mapping)。另外,对于那些生存期相对较长而且重新创建的开销也不高的对象来说,弱引用也比较有用。关键之处在于,垃圾收集器运行时如果碰到了弱可及对象,将释放 WeakReference 引用的对象。然而,请注意,垃圾收集器可能要运行多次才能找到并释放弱可及对象。
PhantomReference 类 PhantomReference 类只能用于跟踪对被引用对象即将进行的收集。同样,它还能用于执行 pre-mortem 清除操作。PhantomReference 必须与 ReferenceQueue 类一起使用。需要 ReferenceQueue 是因为它能够充当通知机制。当垃圾收集器确定了某个对象是虚可及对象时,PhantomReference 对象就被放在它的 ReferenceQueue 上。将 PhantomReference 对象放在 ReferenceQueue 上也就是一个通知,表明 PhantomReference 对象引用的对象已经结束,可供收集了。这使您能够刚好在对象占用的内存被回收之前采取行动。
垃圾收集器和引用交互 垃圾收集器每次运行时都可以随意地释放不再是强可及的对象占用的内存。如果垃圾收集器发现了软可及对象,就会出现下列情况:
SoftReference 对象的 referent 域被设置为 null,从而使该对象不再引用 heap 对象。
SoftReference 引用过的 heap 对象被声明为 finalizable。
当 heap 对象的 finalize() 方法被运行而且该对象占用的内存被释放,SoftReference 对象就被添加到它的 ReferenceQueue(如果后者存在的话)。
如果垃圾收集器发现了弱可及对象,就会出现下列情况:
WeakReference 对象的 referent 域被设置为 null,从而使该对象不再引用 heap 对象。
WeakReference 引用过的 heap 对象被声明为 finalizable。
当 heap 对象的 finalize() 方法被运行而且该对象占用的内存被释放时,WeakReference 对象就被添加到它的 ReferenceQueue(如果后者存在的话)。
如果垃圾收集器发现了虚可及对象,就会出现下列情况:
PhantomReference 引用过的 heap 对象被声明为 finalizable。
与软引用和弱引用有所不同,PhantomReference 在堆对象被释放之前就被添加到它的 ReferenceQueue。(请记住,所有的 PhantomReference 对象都必须用经过关联的 ReferenceQueue 来创建。)这使您能够在堆对象被回收之前采取行动。
请考虑清单 1 中的代码。图 1 说明了这段代码的执行情况。
清单 1. 使用 WeakReference 及 ReferenceQueue 的示例代码
//Create a strong reference to an objectMyObject obj = new MyObject(); //1//Create a reference queueReferenceQueue rq = new ReferenceQueue(); //2 //Create a weakReference to obj and associate our reference queueWeakReference wr = new WeakReference(obj, rq); //3
图 1. 执行了清单 1 中行 //1、//2 和 //3 的代码之后的对象布局
图 1 显示了每行代码执行后各对象的状态。行 //1 创建 MyObject 对象,而行 //2 则创建 ReferenceQueue 对象。行 //3 创建引用其引用对象 MyObject 的 WeakReference 对象,还创建它的 ReferenceQueue。请注意,每个对象引用(obj、rq 及 wr)都是强引用。要利用这些引用类,您必须取消对 MyObject 对象的强引用,方法是将 obj 设置为 null。前面说过,如果不这样做,对象 MyObject 永远都不会被回收,引用类的任何优点都会被削弱。
每个引用类都有一个 get() 方法,而 ReferenceQueue 类有一个 poll() 方法。get() 方法返回对被引用对象的引用。在 PhantomReference 上调用 get() 总是会返回 null。这是因为 PhantomReference 只用于跟踪收集。poll() 方法返回已被添加到队列中的引用对象,如果队列中没有任何对象,它就返回 null。因此,执行清单 1 之后再调用 get() 和 poll() 的结果可能是:
wr.get(); //returns reference to MyObjectrq.poll(); //returns null
现在我们假定垃圾收集器开始运行。由于 MyObject 对象没有被释放,所以 get() 和 poll() 方法将返回同样的值;obj 仍然保持对该对象进行强引用。实际上,对象布局还是没有改变,和图 1 所示的差不多。然而,请考虑下面的代码:
obj = null;System.gc(); //run the collector
在这段代码执行后,对象布局就如图 2 所示:
图 2. obj = null; 和垃圾收集器运行后的对象布局
现在,调用 get() 和 poll() 将产生与前面不同的结果:
wr.get(); //returns nullrq.poll(); //returns a reference to the WeakReference object
这种情况表明,MyObject 对象(对它的引用原来是由 WeakReference 对象进行的)不再可用。这意味着垃圾收集器释放了 MyObject 占用的内存,从而使 WeakReference 对象可以被放在它的 ReferenceQueue 上。这样,您就可以知道当 WeakReference 或 SoftReference 类的 get() 方法返回 null 时,就有一个对象被声明为 finalizable,而且可能(不过不一定)被收集。只有当 heap 对象完全结束而且其内存被回收后,WeakReference 或 SoftReference 才会被放到与其关联的 ReferenceQueue 上。清单 2 显示了一个完整的可运行程序,它展示了这些原理中的一部分。这段代码本身就颇具说明性,它含有很多注释和打印语句,可以帮助您理解。
清单 2. 展示引用类原理的完整程序
import java.lang.ref.*;class MyObject{ protected void finalize() throws Throwable { System.out.println("In finalize method for this object: " this); }}class ReferenceUsage{ public static void main(String args[]) { hold(); release(); } public static void hold() { System.out.println("Example of incorrectly holding a strong " "reference"); //Create an object MyObject obj = new MyObject(); System.out.println("object is " obj); //Create a reference queue ReferenceQueue rq = new ReferenceQueue(); //Create a weakReference to obj and associate our reference queue WeakReference wr = new WeakReference(obj, rq); System.out.println("The weak reference is " wr); //Check to see if it's on the ref queue yet System.out.println("Polling the reference queue returns " rq.poll()); System.out.println("Getting the referent from the " "weak reference returns " wr.get()); System.out.println("Calling GC"); System.gc(); System.out.println("Polling the reference queue returns " rq.poll()); System.out.println("Getting the referent from the " "weak reference returns " wr.get()); } public static void release() { System.out.println(""); System.out.println("Example of correctly releasing a strong " "reference"); //Create an object MyObject obj = new MyObject(); System.out.println("object is " obj); //Create a reference queue ReferenceQueue rq = new ReferenceQueue(); //Create a weakReference to obj and associate our reference queue WeakReference wr = new WeakReference(obj, rq); System.out.println("The weak reference is " wr); //Check to see if it's on the ref queue yet System.out.println("Polling the reference queue returns " rq.poll()); System.out.println("Getting the referent from the " "weak reference returns " wr.get()); System.out.println("Set the obj reference to null and call GC"); obj = null; System.gc(); System.out.println("Polling the reference queue returns " rq.poll()); System.out.println("Getting the referent from the " "weak reference returns " wr.get()); }}共2页。 1 2 :
下载本文示例代码
基于Java 2平台的引用类使用指南基于Java 2平台的引用类使用指南基于Java 2平台的引用类使用指南基于Java 2平台的引用类使用指南基于Java 2平台的引用类使用指南基于Java 2平台的引用类使用指南基于Java 2平台的引用类使用指南基于Java 2平台的引用类使用指南基于Java 2平台的引用类使用指南基于Java 2平台的引用类使用指南基于Java 2平台的引用类使用指南基于Java 2平台的引用类使用指南基于Java 2平台的引用类使用指南基于Java 2平台的引用类使用指南基于Java 2平台的引用类使用指南
阅读(127) | 评论(0) | 转发(0) |