文章目录

  • 1. 强引用
  • 2. 软引用(SoftReference)
  • 3. 弱引用(WeakReference)
  • 4. 虚引用(PhantomReference)
  • 5. 总结



java中对象引用分为强、软、若、虚4中引用,其中强引用就是普通的new一个对象,软引用、弱引用、虚引用都是继承自抽象类Reference。

1. 强引用

程序代码中最常见的引用,比如Persion persion = new Persion(),无论任何情况下,只要强引用关系还在,强引用的对象是可触及的,垃圾回收器就不会回收掉被引用的对象。
如果领强引用关系显示设置null,那么在垃圾回收时就有可能会被回收,具体的回收时机要看垃圾收集策略。正是因为只要强引用的关系存在,就不会被GC,因此强引用是造成java内存泄漏的主要原因之一。
只要强引用关系还在,即使程序抛出OOM异常,不会回收强引用的对象。

例如

String str = new String("hello world");

其中局部变量str指向了堆中的String实例,通过str就可以操作该实例,因此str就是String实例的强引用。

2. 软引用(SoftReference)

软引用关系存在时,如果此时由于内存不足,则会发生GC进行垃圾对象回收,此时是不会回收软引用对象的,因为软引用关系还在,但是当GC后,内存还是不足以分配对象,此时会进行第二次GC,此时会回收掉软引用对象,如果即使回收掉软引用对象内存还是不足,则会发生内存溢出。
软引用是用来描述一些还有用,但非必须的对象。比如高速缓存就是用到软引用,如果还有空闲内存,就可以暂时保留缓存,当内存不足时清理掉缓存,保证程序不至于崩溃。

示例一如下

public static void main(String[] args) {
    /*persion为强引用*/
    Persion persion = new Persion(1, "lzj", 30);
    /*建立persion的软引用*/
    SoftReference<Persion> reference = new SoftReference<>(persion);
    /*令强引用为null,以便垃圾回收时进行回收*/
    persion = null;
    /*获取软引用的对象*/
    System.out.println(reference.get());	//Persion{id=1, name='lzj', age=30}
}

示例二如下:
软引用在构造引用时也可以指定一个引用队列,当软引用关联的对象被回收时,就会把回收的软引用对象加入到指定的引用队列中,通过这个队列可以跟踪软引用关联对象回收情况。注意回收的是软引用关联的对象,加入到软引用队列中的是软引用对象

public static void test2() throws InterruptedException {
    Persion persion = new Persion(1, "lzj", 30);
    ReferenceQueue<Persion> queue = new ReferenceQueue<>();
    SoftReference<Persion> reference = new SoftReference<>(persion, queue);
    System.out.println("软引用对象为:" + reference);
    /*垃圾回收之前,通过软引用可以获取对象的关联对象*/
    System.out.println(reference.get());
    persion = null;
    System.gc();
    Thread.sleep(1000);
    System.out.println(reference.get());

    /*运行之后,该句代码一直阻塞,表示没有软引用关联对象被回收,
    * 调用队列的remove方法,当有一个软引用关联对象被回收时,该方法才会返回,否则就一直阻塞*/
    Reference<? extends Persion> referenceGC = queue.remove();
    if (referenceGC == null){
        System.out.println("说明没有GC,或者GC后,内存空间依然充足,不会回收软引用关联的对象");
    }
}

运行结果如下:

软引用对象为:java.lang.ref.SoftReference@383534aa
Persion{id=1, name='lzj', age=30}
Persion{id=1, name='lzj', age=30}

3. 弱引用(WeakReference)

弱引用也是用来表示一些非必须的对象,被弱引用关联的对象只能生存到下一次垃圾回收器发生为止。GC发生时,不管堆空间内存是否充足,都会回收掉弱引用关联的对象。
弱引用与软引用一样,都非常适合来保存那些可有可无的缓存数据。使用弱引用或软引用后,当系统内存不足时,这些缓存数据会被回收,不会导致内存溢出;当资源充足时,缓存数据可以长时间存在,提高程序执行效率。

示例一如下:

public static void test1(){
    Persion persion = new Persion(1, "lzj", 30);
    WeakReference<Persion> reference = new WeakReference<>(persion);
    persion = null;
    System.out.println(reference.get());	//Persion{id=1, name='lzj', age=30}
}

示例二如下:
弱引用和软引用一样,在构造引用时也可以指定一个引用队列,当弱引用关联的对象被回收时,就会把回收的弱引用对象加入到指定的引用队列中,通过这个队列可以跟踪弱引用关联对象回收情况。注意回收的是弱引用关联的对象,加入到弱引用队列中的是弱引用对象

public static void test2() throws InterruptedException {
    Persion persion = new Persion(1, "lzj", 30);
    /*垃圾回收的弱引用队列*/
    ReferenceQueue<Persion> queue = new ReferenceQueue<>();
    WeakReference<Persion> reference = new WeakReference<>(persion, queue);
    System.out.println("弱引用对象为:" + reference);
    persion = null;
    /*垃圾回收前,弱引用关联的persion对象没被回收,可以获取到*/
    System.out.println("垃圾回收前:" + reference.get());
    /*强制执行GC*/
    System.gc();
    /*程序睡眠一段时间,以便执行GC*/
    Thread.sleep(1000);
    /*垃圾回收后,弱引用关联的persion对象被回收了,获取不到了*/
    System.out.println("垃圾回收后:" + reference.get());

    Reference<? extends Persion> persionGC;
    while ((persionGC = queue.remove()) != null){
        /*垃圾对垒中的persionGC就是前面的reference弱引用对象,而reference是对persion的弱引用,由此可见垃圾回收了persion对象,
        * 注意是回收的弱引用关联的对象,即persion对象
        * */
        System.out.println("垃圾回收队列中回收的弱引用:" + persionGC);
        break;
    }
}

输出如下:

弱引用对象为:java.lang.ref.WeakReference@74a14482
垃圾回收前:Persion{id=1, name='lzj', age=30}
垃圾回收后:null
垃圾回收队列中回收的弱引用:java.lang.ref.WeakReference@74a14482

4. 虚引用(PhantomReference)

虚引用也称为幽灵引用或者幻影引用,是引用类型中最弱的一个。如果一个对象仅持有虚引用,那么它和几乎内有引用是相同的,只要有GC发生,随时会被回收掉。因此一个对象是否持有虚引用,完全不会决定对象的声明周期。
虚应用和前面几种引用不同,无法通过get方法获取关联的对象。为一个对象设置需饮用关联唯一目的在于跟踪垃圾回收过程。比如能在这个对象被垃圾回收器回收时收到一个系统通知。由于虚引用可以跟踪对象回收时间,因此,可以将一些资源释放操作放置在虚引用中执行和记录。

虚引用与前面引用另一个区别就是,虚引用必须与引用队列一起使用。当垃圾回收器准备回收一个对象时,如果发现他还有虚引用就会在回收对象后,将这个虚引用加入到引用队列,以便通知应用程序对象回收情况。

示例如下

```java
public class PhantomReferenceDemo {
    public static Persion persion;
    public static ReferenceQueue<Persion> queue;
    
    public static void main(String[] args) {
        QueueThread queueThread = new QueueThread();
        /*设置守护线程,main线程结束后,queue线程也自动结束*/
        queueThread.setDaemon(true);
        queueThread.start();

        persion = new Persion(1, "lzj", 0);
        queue = new ReferenceQueue<>();
        PhantomReference<Persion> reference = new PhantomReference<>(persion, queue);
        persion = null;
        System.gc();
    }

    public static class QueueThread extends Thread{
        @Override
        public void run() {
            while (true){
                if (queue != null){
                    Reference<? extends Persion> gcObject = null;
                    try {
                        gcObject = queue.remove();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    if (gcObject != null){
                        System.out.println("GC时,gcObject虚引用对象关联的对象被回收了");
                    }
                }
            }

        }
    }
}

5. 总结

强引用:引用关系存在,即可达性分析后为有效对象,就不会被回收,即使发生OOM;
软引用:内存不足时,回收软引用的关联对象;
弱引用:GC时,只要发现了弱引用,就回收弱引用关联对象;
虚引用:虚引用主要是用来做对象回收跟踪的。