弱引用
弱引用也是用来描述非必须对象的,他的强度比软引用更弱一些,被弱引用关联的对象,在垃圾回收时,如果这个对象只被弱引用关联(没有任何强引用关联他),那么这个对象就会被回收。
先看个使用的例子,来分析弱引用何时被回收
/**
* 弱引用关联对象何时被回收
* Created by ccr at 2018/7/14.
*/
public class WeakReferenceDemo {
public static void main(String[] args) throws InterruptedException {
//100M的缓存数据
byte[] cacheData = new byte[100 * 1024 * 1024];
//将缓存数据用软引用持有
WeakReference<byte[]> cacheRef = new WeakReference<>(cacheData);
System.out.println("第一次GC前" + cacheData);
System.out.println("第一次GC前" + cacheRef.get());
//进行一次GC后查看对象的回收情况
System.gc();
//等待GC
Thread.sleep(500);
System.out.println("第一次GC后" + cacheData);
System.out.println("第一次GC后" + cacheRef.get());
//将缓存数据的强引用去除
cacheData = null;
System.gc();
//等待GC
Thread.sleep(500);
System.out.println("第二次GC后" + cacheData);
System.out.println("第二次GC后" + cacheRef.get());
}
}
输出结果:
第一次GC前[B@7d4991ad
第一次GC前[B@7d4991ad
第一次GC后[B@7d4991ad
第一次GC后[B@7d4991ad
第二次GC后null
第二次GC后null
从上面的代码中可以看出,弱引用关联的对象是否回收取决于这个对象有没有其他强引用指向它。这个确实很难理解,我们从源码来分析下是如何实现的。
从代码中可以看到,弱引用调用get方法的时候实际上是返回的referent对象,在jvm执行gc操作以后,因为内存对象不存在强引用,所以会被gc回收,但在回收之前,jvm会找待回收对象的弱引用,并调用clear方法,将referent对象置为null,这样做可能是为了避免内存已经被回收了,但弱引用通过referent继续访问会导致非法访问内存的错误。在clear处加断点,可以发现会在System.gc()方法调用后进入。
既然弱引用关联对象的存活周期和强引用差不多,那直接用强引用好了,干嘛费用弄出个弱引用呢?其实弱引用存在必然有他的应用场景。看下下面的例子。
static Map<Object,Object> container = new HashMap<>();
public static void putToContainer(Object key,Object value){
container.put(key,value);
}
public static void main(String[] args) {
//某个类中有这样一段代码
Object key = new Object();
Object value = new Object();
putToContainer(key,value);
//..........
/**
* 若干调用层次后程序员发现这个key指向的对象没有用了,
* 为了节省内存打算把这个对象抛弃,然而下面这个方式真的能把对象回收掉吗?
* 由于container对象中包含了这个对象的引用,所以这个对象不能按照程序员的意向进行回收.
* 并且由于在程序中的任何部分没有再出现这个键,所以,这个键 / 值 对无法从映射中删除。
* 很可能会造成内存泄漏。
*/
key = null;
}
使用弱引用的场景有哪些,比如JDK动态代理时使用的WeakMap,比如ThreadLocal,比如WeakHashMap,想了解WeakMap怎么用弱引用解决内存泄露问题,可以看链接“基础-JDK动态代理-WeakCache解析”