通过例子理解java强引用,软引用,弱引用,虚引用

前言

在看threadlocal源码的时候碰到了弱引用, 所以本文将用例子来理解java中的四种引用类型.

本文源码下载

强引用

在程序代码中普遍存在,类似Object obj = new Object()这类的引用,只要强引用还存在,垃圾收集器永远不会回收掉被引用的对象.

例子1: 强引用

先解释finalize()方法: 每一个对象的finalize()(从Object继承的方法)都只会被系统自动调用一次,如果对象面临下一次回收,它的finalize()不会被再次执行.

package com.reference.test;

public class FinalizeEscapeGC {
    public static FinalizeEscapeGC SAVE_HOOK = null;
    @Override
    protected void finalize() throws Throwable {
        super.finalize();
        System.out.println("finalize method executed");
        FinalizeEscapeGC.SAVE_HOOK = this;
    }
    public static void helpGC() throws Throwable {
        SAVE_HOOK = null;
        System.gc();
        Thread.sleep(500);
        if (SAVE_HOOK != null) {
            System.out.println("yes, i am still alive.");
        } else {
            System.out.println("no, i am dead.");
        }
    }
    
    public static void main(String[] args) throws Throwable {
        SAVE_HOOK = new FinalizeEscapeGC();
        helpGC(); // 第一次执行了finalize自救
        helpGC(); // finalize执行过了一次便不再执行了
    }
}

结果如下: 在这里这个FinalizeEscapeGC对象有一个强引用SAVE_HOOK指向它, 如果不设置为null,垃圾回收器将不会回收该对象. 主动设置为null, 可以帮助垃圾收集器回收被引用的对象.

finalize method executed
yes, i am still alive.
no, i am dead.

软引用

软引用用来描述一些还有用,但并非必需的对象. 对于软引用关联着的对象,在系统将要发生内存溢出异常之前,将会把这些对象列进回收范围之中并进行第二次回收.如果这次回收还是没有足够的内存,才会抛出内存溢出异常. 在JDK 1.2之后, 提供了SoftRefernce类来实现软引用.

例子2: 软引用

下面的例子中TestSoftReference的一个对象由一个强引用tsr和一个软引用sr共同指向, 然后tsr = null去除强引用,此时还剩下一个软引用指向该对象,主动调用gc方法, 看看会发生什么?按照软引用的定义来看, 在内存还足够的时候不会回收软引用.

package com.reference.test;

import java.lang.ref.SoftReference;
public class TestSoftReference {
    @Override
    protected void finalize() throws Throwable {
        super.finalize();
        System.out.println("finalize method executed");
    }

    public static void main(String[] args) {
        TestSoftReference tsr = new TestSoftReference();
        System.out.println("tsr instance: " + tsr);
        SoftReference sr = new SoftReference(tsr);
        /**
         *  此时TestSoftReference的一个对象有两个引用指向它:
         *  1. 一个强引用tsr
         *  2. 一个软引用sr
         */
        System.out.println("before gc: " + sr.get());
        tsr = null;  // 此时只有一个软引用sr指向该对象
        System.gc(); // 启动垃圾回收器
        System.out.println("after  gc: " + sr.get());
    }
}

结果如下: 可以看到只存在一个软引用指向该对象时, 即使主动调用System.gc()方法也没有回收该对象的内存空间.

tsr instance: com.reference.test.TestSoftReference@15db9742
before gc: com.reference.test.TestSoftReference@15db9742
after  gc: com.reference.test.TestSoftReference@15db9742

弱引用

用来描述非必需对象的,但是它的强度比软引用更弱一些, 被弱引用关联的对象只能生存到下一次垃圾收集发生之前. 当垃圾收集器工作时,无论当前内存是否足够,都会回收掉只被弱引用关联的对象.在JDK 1.2之后,提供了WeakReference类来实现弱引用.

例子3: 弱引用

import java.lang.ref.WeakReference;
public class TestWeakReference {

    @Override
    protected void finalize() throws Throwable {
        super.finalize();
        System.out.println("finalize method executed");
    }

    public static void main(String[] args) {
        TestWeakReference twr = new TestWeakReference();
        WeakReference wr = new WeakReference(twr);
        /**
         *  此时TestSoftReference的一个对象有两个引用指向它:
         *  1. 一个强引用twr
         *  2. 一个弱引用sr
         */
        System.out.println("before gc: " + wr.get());
        twr = null;  //去掉强引用twr
        System.gc();
        System.out.println("after  gc: " + wr.get());
    }
}

结果如下: 可以看到弱引用已经被回收了.

before gc: com.reference.test.TestWeakReference@15db9742
after  gc: null
finalize method executed

接下来就会有个问题, 那弱引用对应的对象被回收了, 那弱引用本身该怎么办呢, 因为它本身也对应了一个WeakReference的对象.

ReferenceQueue

这个ReferenceQueue是专门用来存放引用的, 当软引用,弱引用,虚引用对应的那个对象被回收后的同时,该引用会自动加入到你所定义的ReferenceQueue中.

例子4: 引用队列

import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;

public class TestReferenceQueue {
    public static void main(String[] args) {
        ReferenceQueue rq = new ReferenceQueue();
        WeakReference wr = new WeakReference(new TestReferenceQueue(), rq);
        System.out.println("弱引用对应的对象:" + wr.get() + ", 弱引用本身:" + wr);
        System.out.println("队列中对象:" + rq.poll());
        /**
         * TestReferenceQueue中的对象只有一个引用 就是wr弱引用
         * 因此直接调用gc就可以
         */
        System.gc();
        System.out.println("弱引用对应的对象:" + wr.get() + ", 弱引用本身:" + wr);
        System.out.println("队列中对象:" + rq.poll());
    }
}

结果如下: 在弱引用对应的对象被回收后该弱引用对象本身也进入到了ReferenceQueue中.ReferenceQueue清除失去了弱引用对象的弱引用本身, 软引用,虚引用也是如此.

弱引用对应的对象:com.reference.test.TestReferenceQueue@15db9742, 弱引用本身:java.lang.ref.WeakReference@6d06d69c
队列中对象:null
弱引用对应的对象:null, 弱引用本身:java.lang.ref.WeakReference@6d06d69c
队列中对象:java.lang.ref.WeakReference@6d06d69c

虚引用

虚引用是最弱的一种引用关系. 一个对象是否有虚引用的存在, 完全不会对其生存时间构成影响,也无法通过虚引用来取得一个对象实例. 为一个对象设置虚引用关联的唯一目的就是希望能在这个对象被收集器回收时收到一个系统通知. 在JDK 1.2之后, 提供了PhantomReference类来实现虚引用.

虚引用必须与ReferenceQueue关联起来.

package com.reference.test;

import java.lang.ref.PhantomReference;
import java.lang.ref.ReferenceQueue;

public class TestPhantomReference {
    @Override
    protected void finalize() throws Throwable {
        super.finalize();
        System.out.println("finalize method executed");
    }

    public static void main(String[] args) {
        ReferenceQueue rq = new ReferenceQueue();
        TestWeakReference twr = new TestWeakReference();
        PhantomReference pr = new PhantomReference(twr, rq);
        System.out.println("before gc: " + pr.get() + ", " + rq.poll());
        twr = null;  //去掉强引用twr
        System.gc();
        System.out.println("after  gc: " + pr.get() + "," + rq.poll());
    }
}

结果如下:

before gc: null, null
after  gc: null,null
finalize method executed

参考

1. 深入理解Java虚拟机
2. https://blog.csdn.net/swebin/article/details/78571933

你可能感兴趣的:(通过例子理解java强引用,软引用,弱引用,虚引用)