Java (强/弱/软/虚)引用
一、整体架构
二、强引用(默认支持)
当内存不足,JVM 开始垃圾回收,对于强引用的对象,就算是出现了 OOM 也不会对该对象进行回收,死都不收。强引用是我们最常见的普通对象引用,只要还有强引用指向一个对象,就能表明对象还“活着”,垃圾回收器不会碰这种对象。在Java 中最常见的就是强引用,把一个对象赋给一个引用变量,这个引用变量就是一个强引用。当一个对象被强引用变量引用时,它处于可达状态,它是不可能被垃圾回收机制回收的,即使该对象以后永远都不会被用到 JVM 也不会回收。因此强引用是造成内存泄漏的主要原因之一。对于一个普通对象,如果没有其他引用关系,只要超过引用的作用域或者显示地将强应用设置为null,一般就可以被垃圾收集。(当然具体回收时机是要看垃圾收集策略)。
1 public class ReferenceDemo { 2 public static void main(String[] args) { 3 //o1 强引用(默认) 4 Object o1 = new Object(); 5 //o2 引用赋值 6 Object o2 = o1; 7 //将o1 置为null 8 o1 = null; 9 //触发垃圾回收,回收o1 10 System.gc(); 11 //查看 o2 是否有变化 12 //输出结果:java.lang.Object@5305068a 说明o2 没有被回收,属于强引用 13 System.out.println(o2); 14 } 15 }
三、软引用
软引用是一种相对强引用弱化了一些的引用,需要用 java.lang.ref.SoftReference 类来实现,可以让对象豁免一些垃圾收集。对于弱引用来说:
【1】当系统内存充足时它不会被回收;
1 public class ReferenceDemo { 2 public static void main(String[] args) { 3 //o1 强引用(默认) 4 Object o1 = new Object(); 5 //o2 引用赋值 ************重点*************** 6 SoftReference<Object> softReference = new SoftReference<>(o1); 7 //java.lang.Object@5305068a 8 System.out.println(o1); 9 //将o1 置为null 10 o1 = null; 11 //触发垃圾回收,回收o1 12 System.gc(); 13 //查看 o2 是否有变化 14 //输出结果:java.lang.ref.SoftReference@5305068a 说明softReference 没有被回收,因为内存充足 15 System.out.println(softReference.get()); 16 //输出结果:null 17 System.out.println(o1); 18 } 19 }
【2】当系统内存不充足时它会被回收;代码演示:准备工作,设置内存大小为10m,具体配置如下:
1 @org.junit.Test 2 public void softReference(){ 3 //o1 强引用(默认) 4 Object o1 = new Object(); 5 //o2 引用赋值 6 SoftReference<Object> softReference = new SoftReference<>(o1); 7 //将o1 置为null 8 o1 = null; 9 try { 10 //模拟内存不足,我们通过 Xms和Xmx分配10M 的内存 11 byte[] bytes = new byte[20*1024*1024]; 12 }catch (Exception e){ 13 e.printStackTrace(); 14 }finally { 15 //通过get 方法获取应用对象,输出结果为:null 说明内存不足时对此进行了回收 16 System.out.println(softReference.get()); 17 } 18 }
软引用通常用于对内存敏感的程序中,比如高速缓冲就有用到软引用,内存够用的时候就保留,不够就回收!
四、弱引用
弱引用需要用 java.lang.ref.WeakReference 类实现,它比软引用的生存期更短,对于只有软引用的对象来说,只要垃圾回收机制一运行,不管JVM 内存是否足够,都会回收该对象占用的内存。
1 @org.junit.Test 2 public void weakReference(){ 3 //o1 强引用(默认) 4 Object o1 = new Object(); 5 //o2 引用赋值 6 WeakReference<Object> weakReference = new WeakReference<>(o1); 7 //输出结果:java.lang.Object@1b701da1 8 System.out.println(weakReference.get()); 9 //将o1 置为null 10 o1 = null; 11 System.gc(); 12 //通过get 方法获取应用对象,输出结果为:null 说明只要进行过gc 就可能被回收 13 System.out.println(weakReference.get()); 14 }
五、虚引用
虚引用需要 java.lang.ref.PhantomReference 类来实现。顾名思义,就是形同虚设,与其它几种引用不同,虚引用并不会决定对象的声明周期。如果一个对象仅持有虚引用,那么它就和没有任何引用一样,在任何时候都可能被垃圾回收器回收,它不能单独使用也不能通过它访问对象,虚引用必须和引用队列(ReferenceQueue)联合使用。
虚引用的主要作用是跟踪对象被垃圾回收的状态,仅仅是提供一种确保对象被 finalize 以后,做某些事情的机制。PhantomReference 的 get方法总是返回 null,因此无法访问对应的引用对象。其意义在于说明一个对象已经进入 finalization 阶段,可以被 gc 回收,用来实现比 finalization 机制更灵活的回收操作。
换句话说,设置虚拟引用的唯一目的,就是在这个对象被收集器回收的时候收到一个系统通知或者后续添加进一步处理。Java 技术允许使用 finalize() 方法在垃圾收集器将对象从内存中清除出去之前做必要的清理工作。
1 @org.junit.Test 2 public void phantomReference(){ 3 //o1 强引用(默认) 4 Object o1 = new Object(); 5 //引用队列 6 ReferenceQueue referenceQueue = new ReferenceQueue(); 7 //创建虚引用 8 PhantomReference phantomReference = new PhantomReference<>(o1, referenceQueue); 9 System.out.println("GC 之前==================="); 10 System.out.println("o1 = "+o1); 11 System.out.println("phantomReference = "+phantomReference.get()); 12 System.out.println("referenceQueue = "+referenceQueue.poll()); 13 14 //垃圾回收 15 o1=null; 16 System.gc(); 17 18 System.out.println("GC 之后==================="); 19 System.out.println("o1 = "+o1); 20 System.out.println("phantomReference = "+phantomReference.get()); 21 System.out.println("referenceQueue = "+referenceQueue.poll()); 22 }
输出结果展示: