探索一下Java的引用类型

Java在java.lang.ref中提供了很多reference类型,包括软引用(SoftReference), 弱引用(WeakReference)还有虚引用(PhantomReference)。JVM的垃圾回收器对不同的引用类型有不同的行为,我试着深入探索一下。

探索之前,让我先引入一个类。

非常简单,实例化BigObj大约需要分配1M左右的堆内存;有一个自己的ID;一个自己的方法。

强引用(Strong Reference)

在讲其他引用之前,要说一下强引用。其实强引用就我们一般定义的引用,如:

aObj就是强引用类型,它指向堆中的对象实例。想让JVM回收这个对象,就是去掉所有指向该对象的强引用就可以了。这也是我们大家所熟知的。

执行它(加上-verbose:gc), 我们就可以看到,

第一次GC时,还需要1M左右的内存;但在aObj=null之后,再次GC时,大约有1M的内存被回收了。

软引用(Soft Reference)

下面来看看什么是软引用,看看下面的代码。

和强引用比起来,可能有点不太习惯。首先构造软引用需要给一个强引用,在强引用设置成null时,softObj.get()仍可正常工作。其实你可以认为它就比强引用复杂了一点,要用get()方法取回实例对象。再看,

运行结果,

把softObj置成null, GC做回收。

好像一切都和强引用差不多。但它不止这么简单,还有一个不一样的地方,在内存紧张时(快OutOfMemory时)它也会被回收,所以它比较适合做某种缓存。

让我们实验一下,

我们有一个List,里面全是BigObj对象。执行一下(加上-Xms5m -Xmx5m 设置Heap的大小),结果是,

在内存快耗尽时,JVM尝试回收SoftReferece指向的内存,虽然最后还是耗尽了。有几点要注意,首先List里的都是强类型,所以它不会被GC回收;还要注意一下,

如果没有这一行,BigObj(0)就有一个强引用指向它,因此JVM也不会回收它,这也就失去了SoftReference原有的作用。

弱引用(Weak Reference)

然后是弱引用,基本用法和Soft差不多(如get()),但它真的比Soft还要弱。真接看Code,

运行结果,

可见,在失去所有强引用时,WeakReference也会被回收。

虚引用(PhantomReference)

首先名字很酷,为什么叫虚呢?因为它的get()方法永远返回null。看看Code,

执行结果(-Xms2m -Xmx2m):

结果说明,Phantom和Soft有一样的特点,不会随着主引用不变化而发生GC,主有在内存吃紧时,才会有GC动作。

此外这里还有个新东西,就是ReferenceQueue。这是PhantomReference存在的原因。

引用队列(ReferenceQueue)

其实前面三种引用类型的构造函数都可以接收ReferenceQueue,但PhantomReference是强制需要的。如果使用了它,在堆对象释放之前,它被被放到ReferenceQueue里。这使我们能够在堆对象被回收之前采取行动。

结果:

看了一些文章,感觉不同的JVM对于这几种Reference的支持还是不太一致的。

一些参考文献

  1. http://www.ibm.com/developerworks/cn/java/j-lo-langref/
  2. http://www.ibm.com/developerworks/cn/java/j-refs/

 

发表评论

电子邮件地址不会被公开。 必填项已用*标注