读书人

java.lang.ref 包研讨

发布时间: 2012-11-07 09:56:10 作者: rapoo

java.lang.ref 包探讨

概述

Java.lang.ref 是 Java 类库中比较特殊的一个包,它提供了与 Java 垃圾回收器密切相关的引用类。这些引用类对象可以指向其它对象,但它们不同于一般的引用,因为它们的存在并不防碍 Java 垃圾回收器对它们所指向的对象进行回收。其好处就在于使者可以保持对使用对象的引用,同时 JVM 依然可以在内存不够用的时候对使用对象进行回收。因此这个包在用来实现与缓存相关的应用时特别有用。同时该包也提供了在对象的“可达”性发生改变时,进行提醒的机制。本文通过对该包进行由浅入深的介绍与分析,使读者可以加深对该包的理解,从而更好地利用该包进行开发。

java.lang.ref 包的介绍

我们可以先来看一下 java.lang.ref 这个包的结构,如图 1 所示


图 1. java.lang.ref 包结构
java.lang.ref 包研讨?

该包中各类的继承关系如图 2 所示


图 2. java.lang.ref 包中类的继承关系 :
java.lang.ref 包研讨?

Reference 是一个抽象类,而 SoftReference,WeakReference,PhantomReference 以及 FinalReference 都是继承它的具体类。

接下来我们来分别介绍和分析强引用以及 java.lang.ref 包下各种虚引用的特性及用法。

StrongReference, SoftReference, WeakReference 以及 PhantomReference 的特性及用法

StrongReference:

我们都知道 JVM 中对象是被分配在堆(heap)上的,当程序行动中不再有引用指向这个对象时,这个对象就可以被垃圾回收器所回收。这里所说的引用也就是我们一般意义上申明的对象类型的变量(如 String, Object, ArrayList 等),区别于原始数据类型的变量(如 int, short, long 等)也称为强引用。

在了解虚引用之前,我们一般都是使用强引用来对对象进行引用。如:


清单 1. StrongReference usage
SoftReference:

SoftReference 在“弱引用”中属于最强的引用。SoftReference 所指向的对象,当没有强引用指向它时,会在内存中停留一段的时间,垃圾回收器会根据 JVM 内存的使用情况(内存的紧缺程度)以及 SoftReference 的 get() 方法的调用情况来决定是否对其进行回收。(后面章节会用几个实验进行阐述)

具体使用一般是通过 SoftReference 的构造方法,将需要用弱引用来指向的对象包装起来。当需要使用的时候,调用 SoftReference 的 get() 方法来获取。当对象未被回收时 SoftReference 的 get() 方法会返回该对象的强引用。如下:


清单 2. SoftReference usage
WeakReference:

WeakReference 是弱于 SoftReference 的引用类型。弱引用的特性和基本与软引用相似,区别就在于弱引用所指向的对象只要进行系统垃圾回收,不管内存使用情况如何,永远对其进行回收(get() 方法返回 null)。

完全可以通过和 SoftReference 一样的方式来操作 WeakReference,这里就不再复述。

弱引用有以下特征:

  • 弱引用使用 get() 方法取得对象的强引用从而访问目标对象。
  • 一旦系统内存回收,无论内存是否紧张,弱引用指向的对象都会被回收。
  • 弱引用也可以避免 Heap 内存不足所导致的异常。

    PhantomReference:

    PhantomReference 是所有“弱引用”中最弱的引用类型。不同于软引用和弱引用,虚引用无法通过 get() 方法来取得目标对象的强引用从而使用目标对象,观察源码可以发现 get() 被重写为永远返回 null。

    那虚引用到底有什么作用?其实虚引用主要被用来?跟踪对象被垃圾回收的状态,通过查看引用队列中是否包含对象所对应的虚引用来判断它是否?即将被垃圾回收,从而采取行动。它并不被期待用来取得目标对象的引用,而目标对象被回收前,它的引用会被放入一个 ReferenceQueue 对象中,从而达到跟踪对象垃圾回收的作用。

    所以具体用法和之前两个有所不同,它必须传入一个 ReferenceQueue 对象。当虚引用所引用对象被垃圾回收后,虚引用会被添加到这个队列中。如:


    清单 3. PhantomReference usage
    各种引用类型总结:

    下表对于各种引用类型的特征进行了小结:


    表 1. 引用类型特性总结
    引用类型取得目标对象方式垃圾回收条件是否可能内存泄漏强引用直接调用不回收可能软引用通过 get() 方法视内存情况回收不可能弱引用通过 get() 方法永远回收不可能虚引用无法取得不回收可能

    注意:

    如果想使用这些相对强引用来说较弱的引用来进行对象操作的时候,就必须保证没有强引用指向被操作对象。否则将会被视为强引用指向,不会具有任何的弱引用的特性。

    下一章我们将做 2 个实验来佐证上面这些总结的内容。

    StrongReference, SoftReference, WeakReference 以及 PhantomReference 的各种特性实验分析

    为了更好的描述它们的特性,先以表格进行归纳,再以示例程序加以说明。

    • JVM 使用 Oracle 的 Java SE6
    • 首先将 JVM 运行环境的初始以及最大 Heap 数设到最低以便更明显的看出结果:
      图 3. 设置 JVM 运行环境初始值
      java.lang.ref 包研讨?

      接下来就开始我们的实验。


      表 2:各个引用在 GC 后是否被回收?
      类型GC 后是否回收示例代码运行结果StrongReference不回收见清单 3name:10SoftReference不回收见清单 4name:10WeakReference回收见清单 5name:10PhantomReferenceN/AN/AN/A

      清单 4
      类型是否抛出异常示例代码运行结果StrongReference抛出异常见清单 6Exception in thread "main" java.lang.OutOfMemoryError: Java heap spaceSoftReference不抛异常,之前的引用自动清空并返回 null见清单 7nullWeakReference同上见清单 8nullPhantomReference抛出异常见清单 9Exception in thread "main" java.lang.OutOfMemoryError: Java heap space

      清单 7

      总结:在新开辟 100000 个 Bean 对象时,由于强引用永远不会被系统回收,当最大 Heap 阈值达到 2m 时,系统就会报出 Heap 不足的异常。


      清单 8

      总结:在新开辟 100000 个 Bean 对象时,由于软引用会视内存使用情况来判断是否自动回收,所以当最大 Heap 阈值达到 2m 时,系统自动回收最前面开辟的对象,取第 100 个对象时,返回为 null。


      清单 9

      总结:WeakReference 与 SoftReference 具有相同的特性,也会视内存使用情况来判断是否自动回收。取第 100 个对象时,返回为 null。


      清单 10

      总结:PhantomReference 类似强引用,它不会自动根据内存情况自动对目标对象回收,所以这里在 Heap 里不断开辟新空间,当达到 2m 阈值时,系统报出 OutOfMemoryError 异常。

      FinalReference 以及 Finzlizer

      FinalReference 作为 java.lang.ref 里的一个不能被公开访问的类,又起到了一个什么样的作用呢?作为他的子类, Finalizer 又在垃圾回收机制里扮演了怎么样的角色呢?

      实际上,FinalReference 代表的正是 Java 中的强引用,如这样的代码 :

      Bean bean = new Bean();

      在虚拟机的实现过程中,实际采用了 FinalReference 类对其进行引用。而 Finalizer,除了作为一个实现类外,更是在虚拟机中实现一个 FinalizerThread,以使虚拟机能够在所有的强引用被解除后实现内存清理。

      让我们来看看 Finalizer 是如何工作的。首先,通过声明 FinalizerThread,并将该线程实例化,设置为守护线程后,加入系统线程中去。


      清单 11
      不同 Java 虚拟机上的表现与分析

      让我们来回顾一下四种引用类型的表现以及在垃圾回收器回收清理内存时的表现 .

      1. 软引用 (SoftReference), 引用类型表现为当内存接近满负荷 , 或对象由 SoftReference.get() 方法的调用没有发生一段时间后 , 垃圾回收器将会清理该对象 . 在运行对象的 finalize 方法前 , 会将软引用对象加入 ReferenceQueue 中去 .
      2. 弱引用 (WeakReference), 引用类型表现为当系统垃圾回收器开始回收时 , 则立即会回收该对象的引用 . 与软引用一样 , 弱引用也会在运行对象的 finalize 方法之前将弱引用对象加入 ReferenceQueue.
      3. 强引用 (FinalReference), 这是最常用的引用类型 . JVM 系统采用 Finalizer 来管理每个强引用对象 , 并将其被标记要清理时加入 ReferenceQueue, 并逐一调用该对象的 finalize() 方法 .
      4. 虚引用 (PhantomReference), 这是一个最虚幻的引用类型 . 无论是从哪里都无法再次返回被虚引用所引用的对象 . 虚引用在系统垃圾回收器开始回收对象时 , 将直接调用 finalize() 方法 , 但不会立即将其加入回收队列 . 只有在真正对象被 GC 清除时 , 才会将其加入 Reference 队列中去 .

      这里比较两个比较典型的 JVM 环境,Oracle Java SE6 和 IBM JDK 6。采用了如下的测试代码 :


      清单 13. 类 RefTestObj


      清单 14. 类 RefMainThread

      通过执行 RefMainThread, 我们可以清晰地根据打印结果看到对象在内存中被加入队列 , 以及调用 finalize 方法的顺序及过程 .

      为了测试不同的 JVM 环境并消除其他因素的印象 , 本例采用的背景环境均为 Windows2003 下的 32bit JVM.

      首先采用了环境为 Oracle Java SE 6 update 23 进行测试 , 结果如下 :


      清单 15. Oracle Java SE 6 update 23 下测试结果
       The Reference is java.lang.ref.SoftReference@c17164 and  with object RefTestObj@1fb8ee3[id=1] which is not null  The Reference is java.lang.ref.WeakReferen 

读书人网 >编程

热点推荐