关于Java的四大引用你了解多少?

Java四大引用

强引用

强引用是JVM的默认支持模式,即在引用期间内,一块堆内存空间只要被栈内存中的一个引用引用就无法被GC回收。这种引用除非显示的指定为null,否则即使出现了OurofMemeryError,也不会回收这种引用。

public class test {
    public static void main(String[] args) {
        Object o = new Object();
        Object ref = o;
        o = null;
    //    ref = null;
        System.gc();
        System.out.println(ref);
        }
}

当执行Object ref = o这条语句之后,此时栈中的两个引用:ref、o都引用着new出来的这一块堆内存,要想让这块堆内存回收,必须在System.gc();之前将ref、o这两个引用都置为null,这样当gc发生时,检测到堆内存没有引用引用着它,JVM就知道这块堆内存已经无用,就会用过gc线程去回收它。
上面的代码不会回收开辟的内存空间,因为还有ref在引用这块内存空间,如果将注释去掉,那么这块内堆内存才会被回收。

软引用

SoftReference<K,V>
软引用依靠SoftReference类来实现

public class SoftReference<T> extends Reference<T> {...}

其基本特性与弱引用相同,最大的区别在于如果当前的一块堆内存只有软引用引用着,没有其他引用。那么软引用会尽可能长地保留这块堆内存一直到JVM内存不足是才会被回收

可以看到软引用比强引用要弱,因为当内存满时,GC线程会强制回收只有软引用引用着的堆内存,而不会回收强引用引用着的堆内存,即使会发生OOM!

public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException {
        String str = "cc努力学习";
        SoftReference<String> ref = new SoftReference<>(str);
        str = null;
        System.gc();
        System.out.println(ref.get());   //输出:cc努力学习
        }
}

可以看到这里还是没有回收str,因为当前的内存还不紧张
当执行System.gc()后,如果内存紧张们就会回收软引用中的数据,如果内存不紧张就会一直保留
根据软引用的特点,实现数据缓存功能的时候可以考虑使用软引用。

弱引用

  • WeakReference<T>
  • WeakHashMap<K,V>

弱引用相比软引用多了一个WeakHashMap,属于Map接口的子类,所以也会按照key = value的形式保存:

public class WeakHashMap<K,V>    extends AbstractMap<K,V>    implements Map<K,V> {
            ...
}

正常的Map子类中的数据都是强引用保存,里面的内容始终会保存。如果希望集合可以自动清理暂时不用的数据除了WeakHashMap没有其他引用来指向这块内存空间)可以使用WeakHasnMap类,当进行垃圾收集时释放掉集合中的垃圾信息

		 String key1 = new String("CQUPT");
        String value1 = new String("ccc");
        String key2 = "Zoyp";
        String value2 = "cc";
        Map<String,String> map = new WeakHashMap<>();
        map.put(key1,value1);
        map.put(key2,value2);
        map.put("1","2");
        key1 = null;
        key2 = null;
        System.gc();
        System.out.println(map); //输出:{1=2, Zoyp=cc} 可以看到

注意常量不会被回收
从输出中我们可以看到key1、key2被制定成null后,map中的key1对应的元素被回收了,但key2对应的元素没有被回收。

可以看到,弱引用相比软引用要更弱,只要gc发生,那么只有弱引用引用着的堆内存就会被回收!

弱引用的作用

Java常通过使用弱引用来避免内存泄漏,例如在JDK中有一种内存变量ThreadLocal,通过ThreadLocal变量可以使共享的变量在不同的线程中有不同的副本,原理是在每一个Thread有一个threadLocalMap的属性,用来存放ThreadLocal对象,ThreadLocalMap中是通过一个Entry[]的散列表存放ThreadLocal变量以及ThreadLocal的value,而作为Entry的key的ThreadLocal就是使用的弱引用,结构如下:

static class Entry extends WeakReference<ThreadLocal<?>> {
    /** The value associated with this ThreadLocal. */
    Object value;

    Entry(ThreadLocal<?> k, Object v) {
        super(k);
        value = v;
    }
}

Entry通过继承了WeakReference并通过get、set设置ThreadLocal为Entry的referent。

为什么ThreadLocal内部使用弱引用

原因是如果不使用弱引用,那么当持有value的强引用释放掉后,当线程没有回收释放时,threadLocalMap会一直持有ThreadLocal以及value的强应用,导致value不能够被回收,从而造成内存泄漏。通过使用弱引用,当ThreadLocal的强引用释放掉后,通过一次系统gc检查,发现ThreadLocal对象只有threadLocalMap中Entry的若引用持有,此时根据弱引用的机制就会回收ThreadLocal对象,从而避免了内存泄露。当然ThreadLocal还有一些额外的保护措施,详细分析可以参考:死磕java源码之ThreadLocal实现分析
另外基于Handler的内存泄漏问题,所封装的SafeHandler内部也要使用WeakReference来引用Context,大家如果有兴趣可以我看的另一篇讲解Handelr内存泄漏问题的博客:
Android Handler 机制详解 (三)内存泄漏

虚引用

PhantomReference

虚引用的最大特点就是在其内部保存的引用对象,无论何时取得,结果永远都是null
那大家可能就会问了,既然通过虚引用得到的对象什么时候都为null,那它存在的意义是什么?

为一个对象设置虚引用关联的唯一目的就是能在这个对象被收集器回收时收到一个系统通知

虚引用一般也很少用到,这里就不展开说了

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值