强引用,软引用,弱引用,幻象引用有什么区别?
不同的引用类型,主要体现的是对象的不同的可达性(reachable)状态和对垃圾收集的影响。
- 所谓强引用 (Strong Reference) 就是我们常见的普通对象引用,只要还有强引用指向一个对象,就能表明对象还活着,垃圾回收不回收这种对象。
- 软引用,是一种相对强引用弱化一些的引用,只有当 JVM 认为内存不足时,才会试图回收软引用指向的对象。JVM 会确保在排除 OutOfMemoryError 之前,清理软引用指向的对象,软引用通常用来实现内存敏感的缓存,如果还有空闲内存,就可以暂时保留缓存,当内存不足时,清理掉,这样就保证了使用缓存的同时,不会耗尽内存。
- 弱引用,比软引用拥有更短的生命周期,在垃圾回收线程扫码所管辖的内存区域的过程中,一大发现了只具有弱引用的对象,不管当前内存空间是否足够,都会回收它的内存,由于垃圾回收器是一个优先级,因此不一定很快发现那些只有弱引用的对象。
- 虚引用,形同虚设 ,虚引用不会决定对象的生命周期,如果一个对象仅持有虚引用,其实就和没有任何引用一样。在任何时候都可能被垃圾回收器回收。虚引用和软引用的一个区别是,虚引用必须和引用队列(ReferenceQueue)联合使用。当垃圾回收器准备回收一个对象时,如果发现它还有虚引用,就会在回收对象的内存之前,把这个虚引用加入到与之关联的引用队列中。
四种引用类型对比
引用类型 | 被垃圾回收时间 | 用途 | 生存时间 |
强引用 | 从来不会 | 对象的一般状态 | JVM 停止运行时终止 |
软引用 | 当内存不足时 | 对象缓存 | 内存不足时终止 |
弱引用 | 正常垃圾回收时 | 对象缓存 | 垃圾回收后终止 |
虚引用 | 正常垃圾回收时 | 跟踪对象的垃圾回收 | 垃圾回收后终止 |
四种引用的应用场景
强引用
强引用是使用最普遍的引用,一般声明如下:
Object strongReference = new Object();
如果要对强引用进行垃圾回收,需要设置强引用对象为 null ,或者让其超出对象的生命周期范围,则认为改对象不存在引用。
strongReference = null;
可以看下 ArrayList 是如何进行内存释放的
public void clear() { modCount++; // clear to let GC do its work for (int i = 0; i < size; i++) elementData[i] = null; size = 0; }
软引用
软引用可用来实现内存敏感的高速缓存。
使用如下:
// 强引用 String strongReference = new String("abc"); // 软引用 String str = new String("abc"); SoftReference<String> softReference = new SoftReference<String>(str);
软引用可以和一个引用队列(ReferenceQueue)联合使用,如果软引用所引用的对象被垃圾回收,Java虚拟机就会把这个软引用加入到与之关联的引用队列中。
ReferenceQueue refQueue = new ReferenceQueue(); SoftReference softRef = new SoftReference(obj, refQueue);
软引用垃圾回收
if(JVM内存不足) { // 将软引用中的对象引用置为null str = null; // 通知垃圾回收器进行回收 System.gc(); }
代表软引用的类:java.lang.ref.SoftReference 代表弱引用的类:java.lang.ref.WeakReference 代表虚引用的类:java.lang.ref.PhantomReference 他们同时继承了:java.lang.ref.Reference
public static void soft() throws Exception { Object obj = new Object(); SoftReference<Object> softRef = new SoftReference<Object>(obj); System.out.println(softRef.get()); //有时候会返回Null // java.lang.Object@f9f9d8 System.out.println(refQueue.poll()); // null // 清除强引用,触发GC obj = null; //System.gc(); System.out.println(softRef.get()); //Thread.sleep(200); //System.out.println(refQueue.poll()); }
弱引用
弱引用和软引用的区别在于:弱引用拥有更短暂的生命周期,不管内存够不够,都会回收,都会回收它的内存。
Object obj = new Object(); WeakReference<Object> weakReference = new WeakReference<Object>(obj); obj = null; System.gc(); System.out.println(weakReference.get());// 有时候会返回Null System.out.println(weakReference.isEnqueued());//判断是否有垃圾回收标记,表示即将回收的垃圾
虚引用
// 虚引用 Object obj = new Object(); ReferenceQueue refQueue = new ReferenceQueue(); PhantomReference<Object> phantomReference = new PhantomReference<Object>(obj, refQueue); System.out.println(phantomReference.get()); // 永远返回Null System.out.println(phantomReference.isEnqueued()); //返回时否从队列中删除
- 强可达(Strongly Reachable),就是当一个对象可以有一个或多个线程可以不通过各种引用访问到的情况。比如,我们新创建一个对象,那么创建它的线程对它就是强可达。
- 软可达(Softly Reachable),就是当我们只能通过软引用才能访问到对象的状态。
- 弱可达(Weakly Reachable),类似前面提到的,就是无法通过强引用或者软引用访问,只能通过弱引用访问时的状态。这是十分临近fnalize状态的时机,当弱引用被清除的时候,就符合finalize的条件了。
- 幻象可达(Phantom Reachable),上面流程图已经很直观了,就是没有强、软、弱引用关联,并且finalize过了,只有幻象引用指向这个对象的时候。当然,还有一个最后的状态,就是不可达(unreachable),意味着对象可以被清除了。
Java中4种引用的级别和强度由高到低依次为:强引用 -> 软引用 -> 弱引用 -> 虚引用