引用
Java 虚拟机接管了所有的内存分配与回收工作,极大地减少了程序员的工作量和错误率。GC 在回收内存时,通常采用被称为可达性分析的算法判断一个对象是否可以回收。而在可达性分析中,对象的引用有着决定性的作用。在下图中,GC 从 GC Roots 开始顺着引用链往下寻找对象,发现当前有引用的对象为object 1、object 2、object 3、object 4,而object 5、object 6、object 7虽然互相之间有引用但已经无法从外部引用到。因此,图中 object 1-4 为存活的对象,而 object 5-7 为可回收的对象。
虽然 GC 能够完成垃圾收集工作,但是仍然无法避免 out of memory 。一方面在开发过程中需要注意不再使用的引用设为 null 来释放引用的对象,另一方面也需要从对象引用的角度考虑使用合适的引用类型更好地管理对象内存。
引用的类型
Java 有4种类型的引用:strong(强引用),soft(软引用),weak(弱引用)和 phantom(虚引用)。
强引用:强引用是在 Java 中的普通引用。任何时候我们创建一个新的对象,默认情况下创建一个强引用。例如类的静态变量,从类被初始化之后便已经分配内存,作为强引用对象不能被 GC 回收,需要等待虚拟机退出或类被卸载才能释放引用被 GC 回收。
弱引用:弱引用无法保证对象一定存活于内存中,被弱引用关联的对象只能生存到下一次垃圾收集发生之前。我们可以使用 WeakReference 类来实现弱引用。
软引用:软引用比弱引用稍强一点,垃圾收集发生时弱引用一定会被回收,而软引用会请求 GC 保留自己除非没有其他选择,可以理解为只在将要发生内存溢出时 GC 才会回收软引用。我们可以使用 SoftReference 类来实现软引用。
虚引用:一个对象是否有虚引用的存在,完全不会对其生存时间构成影响,也无法通过虚引用来取得一个对象实例。为一个对象设置虚引用关联的唯一目的就是能在这个对象被 GC 回收时收到一个系统通知。我们可以使用 PhantomReference 类来实现虚引用。当 GC 准备回收一个对象时,如果发现它还有虚引用,就会在回收对象的内存之前,把这个虚引用加入到与之关联的引用队列中。开发者可以通过判断引用队列中是否包含对象来判断对象是否即将被回收,可以在回收之前做些处理。
引用队列:如果引用关联了引用队列,则 GC 回收对象内存的时候会把引用加入到引用队列中。当引用队列中包含引用时,意味着引用指向的堆内存中的对象被回收。
引用的应用
构建缓存
使用软引用可以用于创建 Java 本地高速缓存,只要内存仍然够用缓存就不会被删除,而一旦内存紧张即将溢出时,GC 会删除部分缓存释放内存。例如,创建比较耗时影响性能的数据对象、一段时间内可以重复使用的资源、不常变化的数据等,都可以使用软引用构建缓存,既能保证读取性能,又不会导致内存溢出。
WeakHashMap
WeakHashMap 是以弱引用键实现的哈希表。当 WeakHashMap 中的键不再被强引用使用时,GC 下次回收垃圾时将回收此键。WeakHashMap 中的键被回收后,哈希表的条目也会被 GC 回收。因此,WeakHashMap 可以用于临时存储一些不需要长时间使用的对象,可以有效避免内存溢出。
总结
Java 开发中最常用的引用是强引用,通过new创建对象得到强引用。强引用会阻止 GC 释放对象内存,长时间运行容易导致内存溢出。在开发中,对于强引用变量在使用完毕后应把值设置为 null 来帮助 GC 进行垃圾回收。在 Java 中还有另外三种引用类型,弱引用、软引用和虚引用。弱引用和软引用可以用于构建缓存和避免内存泄露,虚引用可以用于获知对象将被回收的通知并进行处理。
四种引用类型的对比如下:
原文地址:关于Java引用,你必须知道这些