• Java四大引用详解:强引用、软引用、弱引用、虚引用


    面试官考察Java引用会问到强引用、弱引用、软引用、虚引用,具体有什么区别?本篇单独来详解 @mikechen

    Java引用

    从JDK 1.2版本开始,对象的引用被划分为4种级别,从而使程序能更加灵活地控制对象的生命周期,这4种级别由高到低依次为:强引用、软引用、弱引用和虚引用。

    强引用

    强引用是最普遍的引用,一般把一个对象赋给一个引用变量,这个引用变量就是强引用。

    比如:

    1. //  强引用
    2. MikeChen mikechen=new MikeChen();

    在一个方法的内部有一个强引用,这个引用保存在Java栈中,而真正的引用内容(MikeChen)保存在Java堆中。


    如果一个对象具有强引用,垃圾回收器不会回收该对象,当内存空间不足时,JVM 宁愿抛出 OutOfMemoryError异常。

    如果强引用对象不使用时,需要弱化从而使GC能够回收,如下:

    1. //帮助垃圾收集器回收此对象
    2. mikechen=null;

    显式地设置mikechen对象为null,或让其超出对象的生命周期范围,则GC认为该对象不存在引用,这时就可以回收这个对象,具体什么时候收集这要取决于GC算法。

    举例:

    1. package com.mikechen.java.refenence;
    2. /**
    3. * 强引用举例
    4. *
    5. * @author mikechen
    6. */
    7. public class StrongRefenenceDemo {
    8.     public static void main(String[] args) {
    9.         Object o1 = new Object();
    10.         Object o2 = o1;
    11.         o1 = null;
    12.         System.gc();
    13.         System.out.println(o1);  //null
    14.         System.out.println(o2);  //java.lang.Object@2503dbd3
    15.     }
    16. }

    StrongRefenenceDemo 中尽管 o1已经被回收,但是 o2 强引用 o1,一直存在,所以不会被GC回收。

    软引用

    软引用是一种相对强引用弱化了一些的引用,需要用java.lang.ref.SoftReference 类来实现。

    比如:

    1. String str=new String("abc");                                     // 强引用
    2. SoftReference<String> softRef=new SoftReference<String>(str);     // 软引用

    如果一个对象只具有软引用,则内存空间足够,垃圾回收器就不会回收它,如果内存空间不足了,就会回收这些对象的内存。

    先通过一个例子来了解一下软引用:

    1. /**
    2. * 弱引用举例
    3. *
    4. * @author mikechen
    5. */
    6. Object obj = new Object();
    7. SoftReference softRef = new SoftReference<Object>(obj);//删除强引用
    8. obj = null;//调用gc
    9. // 对象依然存在
    10. System.gc();System.out.println("gc之后的值:" + softRef.get());

    软引用可以和一个引用队列(ReferenceQueue)联合使用,如果软引用所引用对象被垃圾回收,Java虚拟机就会把这个软引用加入到与之关联的引用队列中。

    1. ReferenceQueue queue = new ReferenceQueue<>();
    2. Object obj = new Object();
    3. SoftReference softRef = new SoftReference(obj,queue);//删除强引用
    4. obj = null;//调用gc
    5. System.gc();
    6. System.out.println("gc之后的值: " + softRef.get()); // 对象依然存在
    7. //申请较大内存使内存空间使用率达到阈值,强迫gc
    8. byte[] bytes = new byte[100 * 1024 * 1024];//如果obj被回收,则软引用会进入引用队列
    9. Reference reference = queue.remove();if (reference != null){
    10.     System.out.println("对象已被回收: "+ reference.get());  // 对象为null
    11. }
    12. 软引用通常用在对内存敏感的程序中,比如高速缓存就有用到软引用,内存够用的时候就保留,不够用就回收。

      我们看下 Mybatis 缓存类 SoftCache 用到的软引用:

      1. public Object getObject(Object key) {
      2.     Object result = null;
      3.     SoftReference softReference = (SoftReference)this.delegate.getObject(key);
      4.     if (softReference != null) {
      5.         result = softReference.get();
      6.         if (result == null) {
      7.             this.delegate.removeObject(key);
      8.         } else {
      9.             synchronized(this.hardLinksToAvoidGarbageCollection) {
      10.                 this.hardLinksToAvoidGarbageCollection.addFirst(result);
      11.                 if (this.hardLinksToAvoidGarbageCollection.size() > this.numberOfHardLinks) {
      12.                     this.hardLinksToAvoidGarbageCollection.removeLast();
      13.                 }
      14.             }
      15.         }
      16.     }
      17.     return result;}
      18. 注意:软引用对象是在jvm内存不够的时候才会被回收,我们调用System.gc()方法只是起通知作用,JVM什么时候扫描回收对象是JVM自己的状态决定的,就算扫描到软引用对象也不一定会回收它,只有内存不够的时候才会回收。

        弱引用

        弱引用的使用和软引用类似,只是关键字变成了 WeakReference:

        1. MikeChen mikechen = new MikeChen();
        2. WeakReference wr = new WeakReference(mikechen );

        弱引用的特点是不管内存是否足够,只要发生 GC,都会被回收。

        举例说明:

        1. package com.mikechen.java.refenence;
        2. import java.lang.ref.WeakReference;
        3. /**
        4. * 弱引用
        5. *
        6. * @author mikechen
        7. */
        8. public class WeakReferenceDemo {
        9.     public static void main(String[] args) {
        10.         Object o1 = new Object();
        11.         WeakReference<Object> w1 = new WeakReference<Object>(o1);
        12.         System.out.println(o1);
        13.         System.out.println(w1.get());
        14.         o1 = null;
        15.         System.gc();
        16.         System.out.println(o1);
        17.         System.out.println(w1.get());
        18.     }
        19. }

        弱引用的应用

        WeakHashMap

        1. public class WeakHashMapDemo {
        2.     public static void main(String[] args) throws InterruptedException {
        3.         myHashMap();
        4.         myWeakHashMap();
        5.     }
        6.     public static void myHashMap() {
        7.         HashMap map = new HashMap();
        8.         String key = new String("k1");
        9.         String value = "v1";
        10.         map.put(key, value);
        11.         System.out.println(map);
        12.         key = null;
        13.         System.gc();
        14.         System.out.println(map);
        15.     }
        16.     public static void myWeakHashMap() throws InterruptedException {
        17.         WeakHashMap map = new WeakHashMap();
        18.         //String key = "weak";
        19.         // 刚开始写成了上边的代码
        20.         //思考一下,写成上边那样会怎么样? 那可不是引用了
        21.         String key = new String("weak");
        22.         String value = "map";
        23.         map.put(key, value);
        24.         System.out.println(map);
        25.         //去掉强引用
        26.         key = null;
        27.         System.gc();
        28.         Thread.sleep(1000);
        29.         System.out.println(map);
        30.     }}

        当key只有弱引用时,GC发现后会自动清理键和值,作为简单的缓存表解决方案。

        ThreadLocal

        1. static class ThreadLocalMap {
        2.     static class Entry extends WeakReference> {
        3.         Object value;
        4.         Entry(ThreadLocal k, Object v) {
        5.             super(k);
        6.             value = v;
        7.         }
        8.     }
        9.     //......}

        ThreadLocal.ThreadLocalMap.Entry 继承了弱引用,key为当前线程实例,和WeakHashMap基本相同。

        虚引用

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

        虚引用也称为“幽灵引用”或者“幻影引用”,它是最弱的一种引用关系。

        虚引用需要java.lang.ref.PhantomReference 来实现:

        1. A a = new A();
        2. ReferenceQueue<A> rq = new ReferenceQueue<A>();
        3. PhantomReference<A> prA = new PhantomReference<A>(a, rq);

        虚引用主要用来跟踪对象被垃圾回收器回收的活动。

        虚引用与软引用和弱引用的一个区别在于:虚引用必须和引用队列 (ReferenceQueue)联合使用,当垃圾回收器准备回收一个对象时,如果发现它还有虚引用,就会在回收对象的内存之前,把这个虚引用加入到与之 关联的引用队列中。

        Java引用总结

        java4种引用的级别由高到低依次为:强引用 > 软引用 > 弱引用 > 虚引用。

        以上

        作者简介

        陈睿|mikechen,10年+大厂架构经验,《BAT架构技术500期》系列文章作者,分享十余年BAT架构经验以及面试心得!

        阅读mikechen的互联网架构更多技术文章合集

        Java并发|JVM|MySQL|Spring|Redis|分布式|高并发|架构师

        关注「mikechen 的互联网架构」公众号,回复【架构】领取我原创的《300 期 + BAT 架构技术系列与 1000 + 大厂面试题答案》

      19. 相关阅读:
        A. Row GCD(更相减损术+gcd的性质)
        Microsoft Edge浏览器:高效、简洁、个性化的网页浏览体验
        827万!朔黄铁路基于5G边缘计算的智慧牵引变电所研究项目
        [glacierctf 2022] 只会3个
        电容笔有什么用?电容笔十大品牌排行
        Jmeter学习
        wangeditor富文本编辑器的使用(vue)
        在Ubuntu 14.04上如何安装 WordPress 教程
        Golang内存逃逸
        ClickHouse Senior Course Ⅳ
      20. 原文地址:https://blog.csdn.net/ChenRui_yz/article/details/126315260