一、为啥有四种引用?
在Java的世界中,如果
reference类型的数据中存储的数值代表的是另外一块内存的起始地址,就称这块内存代表着一个引用。 这种定义很纯粹,但是太过狭隘,一个对象在这种定义下只有被引用或者没有被引用两种状态,对于如何描述一些“食之无味,弃之可惜”的对象就显得无能为力。 我们希望能描述这样一类对象:当内存空间还足够时,则能保留在内存之中;如果内存空间在进行垃圾收集后还是非常紧张,则可以抛弃这些对象。 很多系统的缓存功能都符合这样的应用场景。所以,把对象的引用分为四种级别,从而使程序能更加灵活的控制对象的生命周期。这四种级别由高到低依次为:强引用、软引用、弱引用和虚引用。
1、强引用(Strong Reference)
强引用就是我们经常使用的引用,其写法如下:
1 import java.io.IOException; 2 3 public class T01_NormalReference { 4 public static void main(String[] args) throws IOException { 5 M m = new M(); 6 m = null; 7 System.gc(); //DisableExplicitGC 8 9 System.in.read(); 10 } 11 }
2、软引用(Soft Reference)
如果一个对象只具有软引用,在内存足够时,垃圾回收器不会回收它;如果内存不足,就会回收这个对象的内存。
使用场景: 图片缓存。图片缓存框架中,“内存缓存”中的图片是以这种引用保存,使得 JVM 在发生 OOM 之前,可以回收这部分缓存。
1 /** 2 * 软引用 3 * 软引用是用来描述一些还有用但并非必须的对象。 4 * 对于软引用关联着的对象,在系统将要发生内存溢出异常之前,将会把这些对象列进回收范围进行第二次回收。 5 * 如果这次回收还没有足够的内存,才会抛出内存溢出异常。 6 * -Xmx20M -Xms20M 7 */ 8 9 import java.lang.ref.SoftReference; 10 11 public class T02_SoftReference { 12 public static void main(String[] args) { 13 SoftReference<byte[]> m = new SoftReference<>(new byte[1024*1024*10]); 14 //m = null; 15 System.out.println(m.get()); 16 System.gc(); 17 try { 18 Thread.sleep(500); 19 } catch (InterruptedException e) { 20 e.printStackTrace(); 21 } 22 System.out.println(m.get()); 23 24 //再分配一个数组,heap将装不下,这时候系统会垃圾回收,先回收一次,如果不够,会把软引用干掉 25 byte[] b = new byte[1024*1024*15]; 26 System.out.println(m.get()); 27 } 28 }
3、弱引用(Weak Reference)
简单来说,就是将对象留在内存的能力不是那么强的引用。当垃圾回收器扫描到只具有弱引用的对象,不管当前内存空间是否足够,都会回收内存。
使用场景:
在下面的代码中,如果类 B 不是虚引用类 A 的话,执行 main 方法会出现内存泄漏的问题, 因为类 B 依然依赖于 A。
1 public class Main { 2 public static void main(String[] args) { 3 4 A a = new A(); 5 B b = new B(a); 6 a = null; 7 System.gc(); 8 System.out.println(b.getA()); // null 9 10 } 11 12 } 13 14 class A {} 15 16 class B { 17 18 WeakReference<A> weakReference; 19 20 public B(A a) { 21 weakReference = new WeakReference<>(a); 22 } 23 24 public A getA() { 25 return weakReference.get(); 26 } 27 }