四种引用(强引用, 软引用, 弱引用, 虚引用)

Java的引用其实只有三种,强引用其实就是不使用引用。

分析

API官方解释

三种引用,逐个变弱:软引用、弱引用、虚引用。
软引用是用来管理内存敏感的缓存引用;
弱引用是用来管理那些需要内存小,不需要阻止键或者值被重新声明的map引用;
虚引用是用来管理那些需要比Java回收机制更灵活的预清理的引用。

API官方

一个对象如果是强可及的,可以不通过任何引用来使用,一个新建的对象对创造它的线程来说是强可及的。
一个对象如果是软可及的,是指它不是强可及的,但可以通过软引用来使用。
一个对象如果是弱可及的,是指它既不是强可及的也不是弱可及的,但可以通过弱引用使用。当指向弱可及的弱引用被回收了,这些弱可及的对象就可以被回收。
一个对象如果是虚可及的,是指它不是强可及的,软可及的,也不是弱可及的。它已经被回收了,但是有一些虚引用指向它。
一个对象如果是不可及的,是指它可以被回收,并且不能被上边的任何手段使用。

四种引用(强引用, 软引用, 弱引用, 虚引用)_第1张图片
软引用

软引用的对象当内存不够的时候会被GC回收,软引用一般是用来管理员内存敏感的缓存。
当GC在某一个时刻决定这个对象是软可及的,它可能就会回收这个对象和其它没有强可及的软引用对象。然后GC会把回收的软引用放入引用队列。
所有的软引用一定会在JVM抛出OOM前回收。除此之外,在其它方面,没有限制说一个软引用会在什么时间被回收,或者说回收的顺序等。JVM通常不会回收那些新创建的或者刚刚被使用的软引用。
只要一个软引用还是强可及的,意思就是说还在被使用,它就不会被回收。所以,一个复杂的缓存应该使用强可及来让保证那些被使用的不被回收,让GC去回收其它的引用。

弱引用

当GC在某一时刻判断一个对象是弱可及的,它会清除指向这个对象的所有弱引用和其它所有不是强可及或者软可及的弱引用。然后回收这些对象。同时把这些引用加入到引用队列。

四种引用(强引用, 软引用, 弱引用, 虚引用)_第2张图片
虚引用

当GC在某一时刻判断这个对象是虚可及的,它会把这个引用加入到引用队列。
为了保证一个对象一直是虚可及的,这个虚引用是不能被找回的,所以get永远返回null。

代码演示

public class FourReferences {

    private List list = new ArrayList<>();
    private SoftReference> softReference = new SoftReference>(new ArrayList());
    private WeakReference> weakReference = new WeakReference>(new ArrayList());
    private PhantomReference> phantomReference = new PhantomReference>(
            new ArrayList(), new ReferenceQueue<>());
    boolean soft = true;
    boolean weak = true;
    boolean strong = true;
    boolean phantom = true;

    public static void main(String[] args) {
        FourReferences fourReferences = new FourReferences();
        int j = 0;
        while (true) {
            fourReferences.weak(j);
            fourReferences.soft(j);
            fourReferences.Strong(j);
            fourReferences.phantom(j);
            j++;
        }

    }

    private void phantom(int j) {
        if (!phantom) {
            return;
        }
        List list2 = phantomReference.get();
        if (list2 == null) {
            System.out.println("phantom对象被回收了" + j);
            phantom = false;
        } else {
            for (int i = 0; i < 99999; i++) {
                list2.add(new String("abc"));
            }
        }
    }

    private void weak(int j) {
        if (!weak) {
            return;
        }
        List list2 = weakReference.get();
        if (list2 == null) {
            System.out.println("weak对象被回收了" + j);
            weak = false;
        } else {
            for (int i = 0; i < 99999; i++) {
                list2.add(new String("abc"));
            }
        }
    }

    private void soft(int j) {
        if (!soft) {
            return;
        }
        List list2 = softReference.get();
        if (list2 == null) {
            System.out.println("soft对象被回收了" + j);
            soft = false;
        } else {
            for (int i = 0; i < 99999; i++) {
                list2.add(new String("abc"));
            }
        }
    }

    private void Strong(int j) {
        if (!strong) {
            return;
        }
        if (list == null) {
            System.out.println("Strong被回收了" + j);
            strong = false;
        } else {
            System.out.print("#" + j + "#");
            for (int i = 0; i < 99999; i++) {
                list.add(new String("abc"));
            }
        }
    }
}
四种引用(强引用, 软引用, 弱引用, 虚引用)_第3张图片
32M内存
四种引用(强引用, 软引用, 弱引用, 虚引用)_第4张图片
64M内存
四种引用(强引用, 软引用, 弱引用, 虚引用)_第5张图片
128M内存

总结

虚引用get返回的永远是null
强引用到OOM都不会被回收。
弱引用相对于软引用来说更容易被回收。其中32M测试时,软引用没有被回收,是因为GC需要回收的时候,此时软引用的list处于强可及状态,没有办法回收,所以OOM。
所有的引用都有一个引用队列,当一个引用被回收的时候GC会把它自动加入到引用队列中。

你可能感兴趣的:(四种引用(强引用, 软引用, 弱引用, 虚引用))