JAVA中的四种引用之二

4、弱引用

         ①弱引用与软引用有点相似,区别在于弱引用所引用的对象的生存期更短。弱引用通过WeakReference类实现,弱引用和软引用很像,但弱引用的引用级别更低。对于只有弱引用的对象而言,当系统垃圾回收机制运行时,不管系统内存是否足够,总会回收该对象所占用的内存。当然,并不是说当一个对象只有弱引用时,它就会立即被回收——正如那些失去引用的对象一样,必须等到系统垃圾回收机制运行时才会被回收。

看下面的例子:

public class WeakReferenceTest {
	public static void main(String args[]) {
		WeakReference<String> weakRef = new WeakReference<String>(new String(
				"test"));
		System.out.println(weakRef.get());
		System.gc();
		System.runFinalization();
		System.out.println(weakRef.get());
	}
}
输出结果:

         test
         null

         注:创建"test"字符串对象时,不要直接用字面值来创建,因为使用字面值来创建字符串时,系统会缓存这个字符串直接量,系统不会回收被缓存的字符串常量。

         ②弱引用具有很大的不确定性,因为每次垃圾回收机制执行时都会回收弱引用所引用的对象,而垃圾回收机制的运行又不受程序员的控制,因此程序获取弱引用所引用的Java对象时必须小心空指针异常——通过弱引用所获取的Java对象可能是null。

         ③由于垃圾回收的不确定性,当程序希望从弱引用中取出被引用对象时,可能这个被引用对象已经被释放了。如果程序需要使用那个被引用的对象,则必须重新创建该对象。这个过程可以采用两种风格的代码完成,下面代码显示了一种风格。

风格1:

String str = weakRef.get();	// 取出弱引用所引用的对象
if (str == null) {
	weakRef = new WeakReference<String>(new String("test"));
	str = weakRef.get();
}
<pre name="code" class="java">//操作str
str = null; // 切断str和弱引用所引用对象之间的联系

 
 

风格2:

String str = weakRef.get(); // 取出弱引用所引用的对象
if (str == null) {
	str = new String("test");
	weakRef = new WeakReference<String>(str);
}
<pre name="code" class="java">// 操作str
str = null; // 切断str和弱引用所引用对象之间的联系

 但是风格1中其实是有点问题的,由于垃圾回收的不确定性,若在if语句块两行代码之间发生了垃圾回收 
 

则会再次将weakRef所引用的对象回收掉,那么weakRef.get()取出来的依然是null

第二段代码则不存在这样的问题。

与WeakReference功能类似的还有WeakHashMap。其实程序很少会考虑直接使用单个的WeakReference来引用某个Java对象,因此这种时候系统内存往往不会特别紧张。当程序有大量的Java对象需要使用弱引用来引用时,可以考虑使用WeakHashMap来保存它们。

在垃圾回收机制运行之前,WeakHashMap的功能与普通的HashMap并没有太大的区别,它们的功能完全相似。但一旦垃圾回收机制被执行,WeakHashMap中所有key-value对都会被清空,除非某些key还有强引用在引用它们。

5、虚引用

软引用和弱引用可以单独使用,但虚引用不能单独使用,单独使用虚引用没有太大的意义。虚引用的主要作用就是跟踪对象被垃圾回收的状态,程序可以通过检查与虚引用关联的引用队列中是否已经包含指定的虚引用,从而了解虚引用所引用对象是否已经被回收。

引用队列由java.lang.ref.ReferenceQueue类表示,它用于保存被回收后对象的引用。当把软引用、弱引用和引用队列联合使用时,系统回收被引用的对象之后,将会把回收对象对应的引用添加到关联的引用队列中。与软引用和弱引用不同的是,虚引用在对象被释放之前,将把它对应的虚引用添加到它的关联的引用队列中,这使得可以在对象被回收之前采取行动

虚引用通过PhantomReference类实现,它完全类似于没有引用。虚引用对对象本身没有太大影响,对象甚至感觉不到虚引用的存在。如果一个对象只有一个虚引用,那它和没有引用的效果大致相同。虚引用主要用于跟踪对象被垃圾回收的状态,虚引用不能单独使用,虚引用必须和引用队列(ReferenceQueue)联合使用。

public class PhantomReferenceTest {

	public static void main(String[] args) {
		ReferenceQueue<String> referenceQueue = new ReferenceQueue<String>();
		String str = new String("test");
		PhantomReference<String> phantomRef = new PhantomReference<String>(str,
				referenceQueue);
		str = null;
		//因为系统无法通过虚引用来获得被引用的对象,所以此处为null
		System.out.println(phantomRef.get());
		System.gc();
		System.runFinalization();
		// 当程序强制垃圾回收后,只有虚引用引用的字符串对象将会被垃圾回收,
		// 当被引用的对象被回收后,对应引用将被添加到关联的引用队列中,
		// 因而将在此处看到输出true。
		System.out.println(referenceQueue.poll() == phantomRef);
	}
}

使用这些引用类可以避免在程序执行期间将对象留在内存中。如果以软引用、弱引用或虚引用的方式引用对象,垃圾回收器就能够随意地释放对象。如果希望尽可能减小程序在其生命周期中所占用的内存大小,这些引用类就很有好处。

最后需要指出的是:要使用这些特殊的引用类,就不能保留对对象的强引用。如果保留了对对象的强引用,就会浪费这些类所提供的所有好处。


你可能感兴趣的:(jvm,虚拟机,垃圾回收,引用)