深入理解JVM(八)——强软弱虚引用

1.强引用

2.软引用

3.弱引用

4.虚引用

5.软引用和弱引用的使用场景

1.强引用
强引用就是我们最常见的对象引用,就是我们平时写的(Person person = new Person();) 强引用指向一个对象,就能表明对象还“活着”,垃圾收集器不会碰这种对象,在JAVA中最常见的引用就是强引用把一个对象赋值给一个引用变量,这个引用变量就是一个强引用,当一个对象被强引用变量引用的时候,它就处于可达状态,它是不可能被垃圾回收机制回收的,即使该对象以后永远不会被用到,JVM和不会回收,因此强引用就是造成JAVA内存泄漏的主要原因之一。

注意点:
对于一个普通的对象,如果没有其它的引用关系,只要通过了引用的作用域或者是显式地将强引用赋值为null。一般认为就是可以被垃圾收集的了。

当内存不足,JVM开始垃圾回收,对于强引用的对象,就算出现了OOM也不会对该对象进行回收。

2.软引用

如果我们写的对象是软引用,那么:
内存足够的前提下,不回收该对象
内存不够的前提下,回收该对象

软引用通常再对内存敏感的程序中,比如高速缓存就有用到软引用。

我们可以用代码示范一下:

首先我们要介绍一个类,软引用类:
SoftReference,传入这个类的引用将会变成软引用,用法如下:

        Object o1 = new Object();
        SoftReference softReference = new SoftReference(o1); 
 

首先是在内存充足的情况下:

    public static void softRefMemoryEnough() {
        Object o1 = new Object();
        SoftReference softReference = new SoftReference(o1);
        //先打印一下对象
        System.out.println(o1);
        System.out.println(softReference.get());
        //将对象置空,在内存够用的情况下,按理来说softReference对象是不会被回收的
        o1 = null;
        System.gc();
        System.out.println(o1);
        System.out.println(softReference.get());
    } 
 

运行结果如下:

深入理解JVM(八)——强软弱虚引用_第1张图片

可以看出,在内存充足的情况下,软引用没有被回收。

接下来我们看一下内存不足的情况下,对象是否会被回收。

    public static void softRefMemoryNotEnough() {
        Object o1 = new Object();
        SoftReference softReference = new SoftReference(o1);
        System.out.println(o1);
        System.out.println(softReference.get());

        o1 = null;
        System.gc();

        try {
            byte[] bytes = new byte[85 * 1024 * 1024];
        } catch (Throwable e) {
            e.printStackTrace();
        } finally {
            System.out.println(o1);
            System.out.println(softReference.get());
        }
    } 
 
    public static void softRefMemoryNotEnough() {
        Object o1 = new Object();
        SoftReference softReference = new SoftReference(o1);
        System.out.println(o1);
        System.out.println(softReference.get());
        o1 = null;
        System.gc();
        try {
            //将JVM的内存设置为100M,然后再创建一个大对象,因为内存不够了,软引用会被回收
            byte[] bytes = new byte[85 * 1024 * 1024];
        } catch (Throwable e) {
            e.printStackTrace();
        } finally {
            System.out.println(o1);
            System.out.println(softReference.get());
        }
    } 
 

运行结果为:
深入理解JVM(八)——强软弱虚引用_第2张图片

可以看出,软引用对象被回收了。

3.弱引用
不管内存是否够用,只要是弱引用,一律回收。

    public static void weakRefMemoryEnough() {
        Object o1 = new Object();
        //弱引用对象
        WeakReference weakReference = new WeakReference(o1);
        System.out.println(o1);
        System.out.println(weakReference.get());

        o1 = null;
        System.gc();

        System.out.println(o1);
        System.out.println(weakReference.get());
    } 
 

运行结果为:

深入理解JVM(八)——强软弱虚引用_第3张图片
可见,两个对象都被回收了

4.虚引用
虚引用是一个比较抽象的概念:

首先,虚引用要PhantomReference类来实现

顾名思义,就是形容虚设,与其他几种引用都不同,虚引用并不会决定对象的生命周期。如果一个对象仅仅持有虚引用,那么它就和没有任何引用一样,在任何时候都可能被垃圾回收器回收,它不能单独使用也不通过它访问对象,虚引用必须和引用队列同时使用。

虚引用的主要作用是跟踪对象被垃圾回收的状态,仅仅是提供了一种确保对象被finalize以后,做某些事情的机制。PhantomReference的get方法总是返回null,因此无法访问对应的引用对象,其意义在于说明一个对象已经进入finalization阶段,可以被gc回收,用来实现比finalization机制更灵活的回收操作。

换句话说,设置虚引用的唯一目的,就是在这个对象呗收集器回收的时候收到一个系统通知或者后序添加进一步的处理。java技术允许使用finalize()方法在垃圾收集器将对象从内存中清除出去之前坐必要的清理工作。

换一句话说,虚引用需要和引用队列一起使用,而且虚引用被回收之后,就会在引用队列里出现。

我们用代码示范一下:

        Object o1 = new Object();
        //开启一个引用队列
        ReferenceQueue queue = new ReferenceQueue<>();
        //虚引用必须和引用队列一起使用
        PhantomReference reference = new PhantomReference<>(o1, queue);

        System.out.println(o1);
        System.out.println(reference.get());
        //还未回收之前,引用队列是空的
        System.out.println(queue.poll());

        System.out.println("================");
        o1 = null;

        System.gc();
        Thread.sleep(500);
        System.out.println(o1);
        System.out.println(reference.get());
        //回收之后,回收的对象到了引用队列里
        System.out.println(queue.poll()); 
 

运行结果为:
深入理解JVM(八)——强软弱虚引用_第4张图片

5.软引用和弱引用的使用场景:

假如有一个应用需要读取大量的本地图片:
如果每次读取图片都从硬盘读取会严重影响性能
如果一次性全部加载到内存中又可能造成内存溢出
此时使用软引用可以解决这个问题:
设计思路:用一个HashMap来保存图片的路径和相应图片对象关联的软引用之间的映射关系,在内存不足时,JVM会自动回收这些缓存图片对象所占用的空间,从而有效避免了OOM的问题。