Java 四种引用类型(强引用、软引用、弱引用、虚引用)

概述

Java 中的引用类似 C 语言中的指针,指向一个对象,比如:

// person 就是指向 Person 实例“张三”的引用
Person person = new Person("张三");

在 JDK1.2 以前,Java 里的引用是很传统的定义:如果 reference 类型的数据中存储的数值代表的是另外一块内存的起始地址,就称该 reference 数据是代表某块内存、某个对象的引用

这种定义当然没有什么不对,但现在看来显得太狭隘了,比如我们希望描述一类对象:当内存空间足够时,能保留在内存中,如果内存空间在进行了垃圾收集后仍然紧张,则可以抛弃这些对象,很多系统的缓存功能都符合这样的应用场景

JDK1.2 对引用的概念作了补充,将引用分为强引用(Strongly Reference)、软引用(SoftReference)、弱引用(Weak Reference)和虚引用(Phantom Reference),强度依次减弱


强引用

Java 中最常见的就是强引用,把一个对象赋给一个引用变量,这个引用变量就是一个强引用。类似 Object obj = new Object()

当一个对象被强引用变量引用时,除非超过了引用的作用域或者显示地将相应强引用赋值为 null,否则是不可能被垃圾回收器回收的


软引用

软引用用来描述一些有用但非必须的对象,此类对象只有在进行一次垃圾收集仍然没有足够内存时,才会在第二次垃圾收集时被回收,需要用 java.lang.ref.SoftReference 类来实现

public class SoftRefenenceDemo {

    public static void main(String[] args) {
        softRefMemoryEnough();
        System.out.println("------------");
        softRefMemoryNotEnough();
    }

    private static void softRefMemoryEnough() {
        Object o1 = new Object();
        SoftReference<Object> s1 = new SoftReference<Object>(o1);
        System.out.println(o1);  // java.lang.Object@2503dbd3
        System.out.println(s1.get());  // java.lang.Object@2503dbd3

        o1 = null;
        System.gc();

        System.out.println(o1);  // null
        System.out.println(s1.get());  // java.lang.Object@2503dbd3
    }

     /**
      * JVM配置 -Xms5m -Xmx5m ,故意 new 一个大对象,使内存不足产生 OOM,看软引用回收情况
      */
    private static void softRefMemoryNotEnough() {
        Object o1 = new Object();
        SoftReference<Object> s1 = new SoftReference<Object>(o1);
        System.out.println(o1);  // java.lang.Object@4b67cf4d
        System.out.println(s1.get());  // java.lang.Object@4b67cf4d

        o1 = null;
        
        try {
          byte[] bytes = new byte[10 * 1024 * 1024];
        } catch(Error e) {
          e.printStackTrace();
        }
        

        System.out.println(o1);  // null
        System.out.println(s1.get());  // null
    }
}

弱引用

弱引用用来描述那些非必须对象,但它的强度比软引用更弱一些。被软引用关联的对象只能生存到下一次垃圾收集发生为止,当垃圾收集器开始工作,无论当前内存是否足够,都会回收掉只被弱引用关联的对象,需要用 java.lang.ref.WeakReference 类来实现

public class WeakReferenceDemo {

    public static void main(String[] args) {
        Object o1 = new Object();
        WeakReference<Object> w1 = new WeakReference<Object>(o1);

        System.out.println(o1);  // java.lang.Object@7440e464
        System.out.println(w1.get());  // java.lang.ref.WeakReference@49476842

        o1 = null;
        System.gc();

        System.out.println(o1);  // null
        System.out.println(w1.get());  // null
    }
}

虚引用

虚引用,顾名思义,就是形同虚设,与其他几种引用都不太一样,一个对象是否有虚引用的存在,完全不会对其生存时间构成影响,也无法通过虚引用来取得一个对象实例。如果一个对象仅持有虚引用,那么它就和没有任何引用一样,在任何时候都可能被垃圾回收器回收。虚引用必须和引用队列(RefenenceQueue)联合使用。虚引用的主要作用是跟踪对象垃圾回收的状态,仅仅是提供了一种确保对象被 finalize 以后,收到一个系统通知或者后续添加进一步的处理

public class PhantomReferenceDemo {

    public static void main(String[] args) throws InterruptedException {
        Object o1 = new Object();
        ReferenceQueue<Object> referenceQueue = new ReferenceQueue<Object>();
        PhantomReference<Object> phantomReference = new PhantomReference<Object>(o1,referenceQueue);

        System.out.println(o1);  // java.lang.Object@7440e464
        System.out.println(referenceQueue.poll());  // null
        System.out.println(phantomReference.get());  // null

        o1 = null;
        System.gc();
        Thread.sleep(3000);

        System.out.println(o1);  // null
        System.out.println(referenceQueue.poll()); // java.lang.ref.PhantomReference@49476842
        System.out.println(phantomReference.get());  // null
    }

}

ReferenceQueue 是用来配合引用工作的,没有ReferenceQueue 一样可以运行。SoftReference、WeakReference、PhantomReference 都有一个可以传递 ReferenceQueue 的构造器。创建引用的时候,可以指定关联的队列,当 GC 释放对象内存的时候,会将引用加入到引用队列。如果程序发现某个虚引用已经被加入到引用队列,那么就可以在所引用的对象的内存被回收之前采取必要的行动,这相当于是一种通知机制。当关联的引用队列中有数据的时候,意味着指向的堆内存中的对象被回收。通过这种方式,JVM 允许我们在对象被销毁后,做一些我们自己想做的事情

热门相关:你好,墨先生   至尊剑皇   至尊剑皇   你好,墨先生   你好,墨先生