关于java:Java四大引用详解强引用软引用弱引用虚引用

38次阅读

共计 5104 个字符,预计需要花费 13 分钟才能阅读完成。

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

Java 援用

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

强援用

强援用是最广泛的援用,个别把一个对象赋给一个援用变量,这个援用变量就是强援用。

比方:

//  强援用
MikeChen mikechen=new MikeChen();

在一个办法的外部有一个强援用,这个援用保留在 Java 栈中,而真正的援用内容 (MikeChen) 保留在 Java 堆中。\
\
如果一个对象具备强援用,垃圾回收器不会回收该对象,当内存空间有余时,JVM 宁愿抛出 OutOfMemoryError 异样。

如果强援用对象不应用时,须要弱化从而使 GC 可能回收,如下:

// 帮忙垃圾收集器回收此对象
mikechen=null;

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

举例:

package com.mikechen.java.refenence;

/**
* 强援用举例
*
* @author mikechen
*/
public class StrongRefenenceDemo {public static void main(String[] args) {Object o1 = new Object();
        Object o2 = o1;
        o1 = null;
        System.gc();
        System.out.println(o1);  //null
        System.out.println(o2);  //java.lang.Object@2503dbd3
    }
}

StrongRefenenceDemo 中只管 o1 曾经被回收,然而 o2 强援用 o1,始终存在,所以不会被 GC 回收。

 

软援用

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

比方:

String str=new String("abc");                                     // 强援用
SoftReference<String> softRef=new SoftReference<String>(str);     // 软援用

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

先通过一个例子来理解一下软援用:

/**
* 弱援用举例
*
* @author mikechen
*/
Object obj = new Object();
SoftReference softRef = new SoftReference<Object>(obj);// 删除强援用
obj = null;// 调用 gc

// 对象仍然存在
System.gc();System.out.println("gc 之后的值:" + softRef.get());

软援用能够和一个援用队列 (ReferenceQueue) 联结应用, 如果软援用所援用对象被垃圾回收,Java 虚拟机就会把这个软援用退出到与之关联的援用队列中。

ReferenceQueue<Object> queue = new ReferenceQueue<>();
Object obj = new Object();
SoftReference softRef = new SoftReference<Object>(obj,queue);// 删除强援用
obj = null;// 调用 gc
System.gc();
System.out.println("gc 之后的值:" + softRef.get()); // 对象仍然存在
// 申请较大内存使内存空间使用率达到阈值,强制 gc
byte[] bytes = new byte[100 * 1024 * 1024];// 如果 obj 被回收,则软援用会进入援用队列
Reference<?> reference = queue.remove();if (reference != null){System.out.println("对象已被回收:"+ reference.get());  // 对象为 null
}

软援用通常用在对内存敏感的程序中,比方高速缓存就有用到软援用,内存够用的时候就保留,不够用就回收。

咱们看下 Mybatis 缓存类 SoftCache 用到的软援用:

public Object getObject(Object key) {
    Object result = null;
    SoftReference<Object> softReference = (SoftReference)this.delegate.getObject(key);
    if (softReference != null) {result = softReference.get();
        if (result == null) {this.delegate.removeObject(key);
        } else {synchronized(this.hardLinksToAvoidGarbageCollection) {this.hardLinksToAvoidGarbageCollection.addFirst(result);
                if (this.hardLinksToAvoidGarbageCollection.size() > this.numberOfHardLinks) {this.hardLinksToAvoidGarbageCollection.removeLast();
                }
            }
        }
    }
    return result;}

留神:软援用对象是在 jvm 内存不够的时候才会被回收,咱们调用 System.gc()办法只是起告诉作用,JVM 什么时候扫描回收对象是 JVM 本人的状态决定的,就算扫描到软援用对象也不肯定会回收它,只有内存不够的时候才会回收。

 

弱援用

弱援用的应用和软援用相似,只是关键字变成了 WeakReference:

MikeChen mikechen = new MikeChen();
WeakReference<MikeChen> wr = new WeakReference<MikeChen>(mikechen);

弱援用的特点是不论内存是否足够,只有产生 GC,都会被回收。

举例说明:

package com.mikechen.java.refenence;

import java.lang.ref.WeakReference;

/**
* 弱援用
*
* @author mikechen
*/
public class WeakReferenceDemo {public static void main(String[] args) {Object o1 = new Object();
        WeakReference<Object> w1 = new WeakReference<Object>(o1);

        System.out.println(o1);
        System.out.println(w1.get());

        o1 = null;
        System.gc();

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

 

弱援用的利用

WeakHashMap

public class WeakHashMapDemo {public static void main(String[] args) throws InterruptedException {myHashMap();
        myWeakHashMap();}

    public static void myHashMap() {HashMap<String, String> map = new HashMap<String, String>();
        String key = new String("k1");
        String value = "v1";
        map.put(key, value);
        System.out.println(map);

        key = null;
        System.gc();

        System.out.println(map);
    }

    public static void myWeakHashMap() throws InterruptedException {WeakHashMap<String, String> map = new WeakHashMap<String, String>();
        //String key = "weak";
        // 刚开始写成了上边的代码
        // 思考一下,写成上边那样会怎么样?那可不是援用了
        String key = new String("weak");
        String value = "map";
        map.put(key, value);
        System.out.println(map);
        // 去掉强援用
        key = null;
        System.gc();
        Thread.sleep(1000);
        System.out.println(map);
    }}

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

ThreadLocal

static class ThreadLocalMap {

    static class Entry extends WeakReference<ThreadLocal<?>> {
        Object value;

        Entry(ThreadLocal<?> k, Object v) {super(k);
            value = v;
        }
    }
    //......}

ThreadLocal.ThreadLocalMap.Entry 继承了弱援用,key 为以后线程实例,和 WeakHashMap 基本相同。

 

虚援用

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

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

虚援用须要 java.lang.ref.PhantomReference 来实现:

A a = new A();
ReferenceQueue<A> rq = new ReferenceQueue<A>();
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 + 大厂面试题答案》

正文完
 0