在我的上一篇文章中,我用一个简单的程序介绍了怎样使用Java的引用对象SoftReference和WeakReference,来建立弱引用并用它们将内存块标记为可回收的。现在让我们来看一个稍微更有用一些的例子,这个例子展示了怎样用引用对象来给一个应用程序提供简单而有效的缓存功能。
当一个引用对象所持有的弱引用被清除时,它值会是null。如果想自动在弱引用被清除时移除此引用对象,又该怎样做呢?Java平台以“引用队列(reference queue)”的形式支持了这种能力:引用队列是java.lang.ref.ReferenceQueue类的实例。当一个弱引用被创建时,可以将它注册到一个引用队列当中,于是当此引用被清除时(即,当它指向的对象即将被垃圾收集时),它将被添加到注册的引用队列中去。
引用队列同时提供了阻塞和非阻塞的方法来从中移除弱引用。你可以采用多种策略来遍历队列并完成任何必要的清除工作,比如在程序的关键点对队列进行选举(polling)或者用一个专门的线程。
清单 B是我在上一篇文章用过的内存消耗类MemoryBlock,而清单 A的例子程序创建越来越大的MemoryBlock对象来耗费内存,并通过引用对象来持有它们的引用,这样它们占用的内存就能够在必要时被释放。例子程序中用了一个自定义的引用类,MyReference,它的源码在清单 C中,这个类继承自SoftReference。
如你所见,每个MyReference对象都在创建时就被注册到一个引用队列当中。当那些MemoryBlock被垃圾收集时,它们相应的引用对象会被添加到引用队列中去。在打印出ArrayList的内容之前,我遍历引用队列以便从ArrayList中移除那些null引用。
图 A是例子程序的输出。请注意ArrayList中仅保持了指向有效的MemoryBlock的引用对象;那些已被垃圾收集的MemoryBlock所对应的引用对象已经被移除。
图 A