java中虚引用PhantomReference与弱引用WeakReference(软引用SoftReference)的差别
来源:互联网 发布:软件代理商协议 编辑:程序博客网 时间:2024/05/29 19:14
之前的这篇博客介绍了java中4种引用的差别和使用场景,在最后的总结中提到:
“软引用和弱引用差别不大,JVM都是先把SoftReference和WeakReference中的referent字段值设置成null,之后加入到引用队列;而虚引用则不同,如果某个堆中的对象,只有虚引用,那么JVM会将PhantomReference加入到引用队列中,JVM不会自动将referent字段值设置成null”。这段总结写的比较仓促,也没有给出实际的例子加以佐证。本文主要是重申下这几种引用的差别,并给出实际的例子,让读者清楚的感受到它们的差别。
软引用和弱引用差别不大,JVM都是先将其referent字段设置成null,之后将软引用或弱引用,加入到关联的引用队列中。我们可以认为JVM先回收堆对象占用的内存,然后才将软引用或弱引用加入到引用队列。
而虚引用则不同,JVM不会自动将虚引用的referent字段设置成null,而是先保留堆对象的内存空间,直接将PhantomReference加入到关联的引用队列,也就是说如果我们不手动调用PhantomReference.clear(),虚引用指向的堆对象内存是不会被释放的。
referent是java.lang.ref.Reference类的私有字段,虽然没有暴露出共有API来访问这个字段,但是我们可以通过反射拿到这个字段的值,这样就能知道引用被加入到引用队列的时候,referent到底是不是null。SoftReference和WeakReference是一样的,这里我们以WeakReference为例。
package ref.referent;import java.lang.ref.Reference;import java.lang.ref.ReferenceQueue;import java.lang.ref.WeakReference;import java.lang.reflect.Field;// 会报空指针:WeakReference中的referent被设置成null,之后加入到ReferenceQueuepublic class TestWeakReference{private static volatile boolean isRun = true;private static volatile ReferenceQueue<String> referenceQueue = new ReferenceQueue<String>();public static void main(String[] args) throws Exception{String abc = new String("abc");System.out.println(abc.getClass() + "@" + abc.hashCode());new Thread() {public void run(){while (isRun){Object o = referenceQueue.poll();if (o != null){try{Field rereferent = Reference.class.getDeclaredField("referent");rereferent.setAccessible(true);Object result = rereferent.get(o);System.out.println("gc will collect:"+ result.getClass() + "@"+ result.hashCode());} catch (Exception e){e.printStackTrace();}}}}}.start();// 对象是弱可达的WeakReference<String> weak = new WeakReference<String>(abc,referenceQueue);System.out.println("weak=" + weak);// 清除强引用,触发GCabc = null;System.gc();Thread.sleep(3000);isRun = false;}}运行这段代码会发现,我们创建的Thread中报空指针异常。当我们清除强引用,触发GC的时候,JVM检测到new String("abc")这个堆中的对象只有WeakReference,那么JVM会释放堆对象的内存,并自动将WeakReference的referent字段设置成null,所以result.getClass()会报空指针异常。
代码与上面类似, 我们将WeakReference替换成PhantomReference:
package ref.referent;import java.lang.ref.PhantomReference;import java.lang.ref.Reference;import java.lang.ref.ReferenceQueue;import java.lang.reflect.Field;// 当PhantomReference加入到ReferenceQueue的时候,目标对象内存空间仍然存在不会被回收.// PhantomReference中的referent字段不会被JVM自动设置成null// 当目标对象的PhantomReference加入到ReferenceQueue的时,此时目标对象是强可达的public class TestPhantomReference{private static volatile boolean isRun = true;private static volatile ReferenceQueue<String> referenceQueue = new ReferenceQueue<String>();public static void main(String[] args) throws Exception{String abc = new String("abc");System.out.println(abc.getClass() + "@" + abc.hashCode());new Thread() {public void run(){while (isRun){Object o = referenceQueue.poll();if (o != null){try{Field rereferent = Reference.class.getDeclaredField("referent");rereferent.setAccessible(true);Object result = rereferent.get(o);System.out.println("gc will collect:"+ result.getClass() + "@"+ result.hashCode());} catch (Exception e){e.printStackTrace();}}}}}.start();// 测试情况1:对象是虚可达的PhantomReference<String> phantom = new PhantomReference<String>(abc,referenceQueue);System.out.println("phantom=" + phantom);// 测试情况2:对象是不可达的,直接就被回收了,不会加入到引用队列// new PhantomReference<String>(abc, referenceQueue);// 清除强引用,触发GCabc = null;System.gc();Thread.sleep(3000);isRun = false;}}运行这段代码会发现,程序没有报异常,执行结果是:
class java.lang.String@96354phantom=java.lang.ref.PhantomReference@15b7986gc will collect:class java.lang.String@96354
很明显,当PhantomReference加入到引用队列的时候,referent字段的值并不是null,而且堆对象占用的内存空间仍然存在。也就是说对于虚引用,JVM是先将其加入引用队列,当我们从引用队列删除PhantomReference对象之后(此时堆中的对象是unreachable的),那么JVM才会释放堆对象占用的内存空间。由此可见,使用虚引用有潜在的内存泄露风险,因为JVM不会自动帮助我们释放,我们必须要保证它指向的堆对象是不可达的。从这点来看,虚引用其实就是强引用,当内存不足的时候,JVM不会自动释放堆对象占用的内存。后续的帖子我会进行一些OOM相关的实验,去证明虚引用的确会导致OOM,而软引用和弱引用则不会导致OOM。
小结:
上面的测试代码,只是为了帮助我们看清楚虚引用与软引用/弱引用的不同表现。在实际的开发中,我们是不会通过反射获取referent字段的值,这样做毫无意义,也不值得提倡。
- java中虚引用PhantomReference与弱引用WeakReference(软引用SoftReference)的差别
- JAVA中的强引用、软引用(SoftReference)、弱引用(WeakReference)和幽灵引用(PhantomReference)
- Java的引用StrongReference、 SoftReference、 WeakReference 、PhantomReference
- Java的引用StrongReference、 SoftReference、 WeakReference 、PhantomReference
- Android内存管理 强引用HardReference、弱引用WeakReference、软引用SoftReference和虚引用PhantomReference
- 强引用(New)、软引用(SoftReference )、弱引用(WeakReference )、虚引用(PhantomReference)
- Java引用对象SoftReference WeakReference PhantomReference
- Java引用对象SoftReference WeakReference PhantomReference
- Java引用对象SoftReference WeakReference PhantomReference
- Java引用对象SoftReference WeakReference PhantomReference
- Java引用总结--StrongReference、SoftReference、WeakReference、PhantomReference
- Java 引用分类:StrongReference、SoftReference、WeakReference、PhantomReference
- Java引用总结--StrongReference、SoftReference、WeakReference、PhantomReference
- Java引用总结--StrongReference、SoftReference、WeakReference、PhantomReference
- Java引用总结--StrongReference、SoftReference、WeakReference、PhantomReference
- Java引用--StrongReference、SoftReference、WeakReference、PhantomReference
- Java引用总结--StrongReference、SoftReference、WeakReference、PhantomReference
- Java引用总结--StrongReference、SoftReference、WeakReference、PhantomReference
- 正则表达式在ABAP中的应用
- 以 vim 的方式来使用 chrome 浏览器(利用 vimium 插件)
- usaco1.1.3的题解
- C++按行读取文本文件,并将每行字符串拆分为double value的坐标值
- Android之TextView、EditText控件显示表情图片
- java中虚引用PhantomReference与弱引用WeakReference(软引用SoftReference)的差别
- map
- 简单文章发布系统(6)
- Android ZXing 二维码、条形码扫描介绍
- three.js 源码注释(十八)Math/Triangle.js
- 走,带上妹子,咱一起去阿里!
- Android动画之translate(位移动画)
- CodeForces 486E LIS of Sequence
- 【大话一角】——六兄弟