程序员必知系列之四种引用的区别以及实战

226 阅读3分钟

这是我参与 8 月更文挑战的第 13 天,活动详情查看: 8月更文挑战

1.什么是引用

所谓的引用,在JAVA中,被定义为指向对象实例地址。

2.引用的分类

2.1 强引用

新建的对象直接赋值给引用变量。这是JAVA默认模式。 注意当内存溢出的时候,JVM不会对该对象进行回收。

2.2 弱引用

弱引用需要用java.lang.ref.WeakReference类来实现,它比软引用的生存期更短 对于只有弱引用的对象来说,只要垃圾回收机制运行,不管JVM的内存空间是否足够,都会回收该对象占用的内存。

2.3 软引用

软引用时一种相对强引用弱化了一些的引用,需要用java.lang.ref.SoftReference类实现,可以在让对象豁免一些垃圾收集。 对于只有软引用的对象来说, 当系统内存充足时,它不会被回收 当系统内存不足时,它会被回收 软引用通常在对内存敏感的程序中,比如高速缓存就有用到软引用,内存够用的时候就保留,不够用就回收。

2.4 虚引用

虚引用需要java.lang.ref.PhantomRefernce类实现 顾名思义,就是形同虚设,与其他几种引用不用,虚引用并不会决定对象的生命周期。 如果一个对象仅持有虚引用,那么它就和没有任何引用一样,在任何时候都可能被垃圾回收器回收,它不能单独使用也不能通过它访问对象,虚引用必须和引用队列联合使用 虚引用的主要作用是跟踪对象被垃圾回收的状态,仅仅是提供一种确保对象被finalize以后,做某些事情的机制。PhantomReference的get方法总是返回null,因此无法访问对应的引用对象,其意义在于说明一个对象已经进入finalization的阶段,可以被gc回收,用来实现比finalize()方法在垃圾收集器将对象从内存中清除出去之前做必要的清理工作。

3.引用的代码验证

3.1 强引用

public class StrongReferenceDemo {

    public static void main(String[] args) {
        Object o1 = new Object();
        Object o2 = o1;
        o1 = null;
        System.gc();
        System.out.println(o2);
    }
}

3.2 弱引用

public class WeakReferenceDemo {

public static void main(String[] args) {
        Object o1 = new Object();
        WeakReference<Object> 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());
    }
}

3.3 软引用

public class SoftReferenceDemo {

    public static void softRef_Memory_Enough() {
        Object o1 = new Object();
        SoftReference<Object> softReference = new SoftReference<>(o1);
        System.out.println(o1);
        System.out.println(softReference.get());
        o1 = null;
        System.gc();
        System.out.println(o1);
        System.out.println(softReference.get());
    }

    /**
     * 故意产生大对象并配置小内存,让它内存不够用导致OOM,看软引用的回收情况
     * -Xms5m -Xmx5m -XX:+PrintGCDetails
     */
    public static void softRef_Memory_NotEnough() {
        Object o1 = new Object();
        SoftReference<Object> softReference = new SoftReference<>(o1);
        System.out.println(o1);
        System.out.println(softReference.get());
        o1 = null;
        try {
            byte[] bytes = new byte[30 * 1024 * 1024];
        } catch (Throwable e) {
            e.printStackTrace();
        } finally {
            System.out.println(o1);
            System.out.println(softReference.get());
        }
    }

    public static void main(String[] args) {
        softRef_Memory_Enough();
    }
}

3.4 虚引用

public class PhantomReferenceDemo {

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

    System.out.println(o1);
    System.out.println(reference.get());
    System.out.println(referenceQueue.poll());

    o1 = null;
    System.gc();
    System.out.println("------------------");
    System.out.println(o1);
    System.out.println(reference.get());
    System.out.println(referenceQueue.poll());
}
}

4.引用的总结

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