注意,以下所述源码版本为 JDK 1.8.0_212
Java中的数据类型分为:
基本数据类型:byte、short、int、long、float、double 8种。
引用类型:上述基本数据类型的包装类、其他各种对象类型。如Integer、Object等。
当说到“引用”时,指的可能是 引用类型 或 一个引用类型的变量,具体视上下文而定。
在JDK1.2之前,Java中的引用的定义是十分传统的:如果reference类型的数据中存储的数值代表的是另一块内存的起始地址,就称这块内存代表着一个引用。在这种定义之下,一个对象只有被引用和没有被引用两种状态,用户代码中无法在对象被GC回收后做一些额外的工作(如清理堆外内存等)。
实际上,我们更希望存在这样的一类对象:当内存空间还足够的时候,这些对象能够保留在内存空间中;如果当内存空间在进行了垃圾收集之后还是非常紧张,则可以抛弃这些对象。基于这种特性,可以满足很多系统的缓存功能的使用场景。
从JDK 1.2起,对引用概念进行了扩充,将引用分为 强引用(Strong Reference)、软引用(Soft Reference)、弱引用(Weak Reference)、虚引用(Phantom Reference)、终引用(Final Reference)。其中强引用就是JDK 1.2之前的引用,日常代码中绝大多数引用都是强引用;而其他几种引用则是JDK 1.2引进的,通过这些新引入的引用可以在用户代码中实现类似“感知到对象被回收从而做一些额外工作”的效果。
新引入的引用是java.lang.Reference类的子类(强引用则不是),其间的关系如下:
注:
强引用没有对应的类型表示,也就是说强引用是普遍存在的,如Object object = new Object(); 。
直接继承java.lang.ref.Reference创建自定义的引用类型是无效的,但是可以直接继承已经存在的引用类型,如sun.misc.Cleaner就是继承自java.lang.ref.PhantomReference。
FinalReference 是package private的作用域的,因此 FinalReference、Finalizer 都无法在用户代码中直接使用,而是由JVM去创建的。
Cleaner位于 sun.misc 包下而非 java.lang.ref 。
2 引用的创建
直接定义的引用变量是强引用,如 Integer a = new Integer() ,a是强引用。
其他几种引用则需要通过Reference子类创建,如 WeakReference<Integer> wr = new WeakReference<>(a); 。这里wr仍是强引用,wr对象内的referent成员才是弱引用。
3 引用对象的可达性及回收
对象间互相引用形成引用链。不同引用间的强弱关系依次是 强引用 > 软引用 > 弱引用 > 虚引用 > 终引用,如果有更强的引用关系存在,那么引用链的可达性将由更强的引用关系决定。因此,可达性就分为:
强可达:对象与GC Root间存在强引用链。例如下图的A、B。
软可达:对象与GC Root间不存在强引用链,但存在软引用链。例如下图的E。
弱可达:对象与GC Root间不存在强、软引用链,但存在弱引用链。例如下图的F、C、D。
虚可达:对象与GC Root间不存在强、软、弱引用链,但存在虚引用链。例如下图的G。
不可达:对象与GC Root间不存在任何引用链。例如下图的H、I。
强引用(日常代码里的引用类型变量几乎都是这种引用)的对象不会被回收(严格来说也不是绝对的,如软、弱、虚引用对象内部对实际对象的引用referent也是强引用,但说强引用时一般不包括此情形),其他引用对象则可能被回收。
软引用:软引用对象在系统将要发生内存耗尽(OOM)前会被回收。示例:
1 // VM参数:-Xmx4m -Xms4m 2 public class SoftReferenceMain { 3 4 public static void main(String[] args) throws Exception { 5 ReferenceQueue<SoftReferenceObject> queue = new ReferenceQueue<>(); 6 SoftReferenceObject object = new SoftReferenceObject(); 7 SoftReference<SoftReferenceObject> reference = new SoftReference<>(object, queue); 8 object = null; 9 System.gc(); 10 Thread.sleep(500); 11 System.out.println(reference.get()); 12 } 13 14 private static class SoftReferenceObject { 15 16 int[] array = new int[120_000]; 17 18 @Override 19 public String toString() { 20 return "SoftReferenceObject"; 21 } 22 } 23 } 24 // 运行后输出结果,可见GC后软引用关联的对象被回收了 25 null