Java 中的四种引用

以前咱们提到过 GC,但当 Java 中引用的对象愈来愈多,会致使内存空间不足,最终会产生错误 OutOfMemoryError,并让应用程序终止。那为何 GC 在此时不能多收集一些对象呢?这就和今天说的引用类型有关了。 git

首先,从 JDK1.2 开始,对象的引用被划分为4种级别,从而使程序能更加灵活地控制对象的生命周期。这4种级别由高到低依次为:强引用软引用弱引用虚引用github

强引用

强引用(Strong Reference)是使用最广泛的引用。若是一个对象具备强引用,那么它永远不会被 GC。例如:缓存

Object strongReference = new Object();复制代码

当内存空间不足时,JVM 宁愿抛出OutOfMemoryError,使程序异常终止,也不会靠随意回收具备强引用的对象来解决内存不足的问题。ide

若是强引用对象不使用时,须要弱化从而能够被 GC,例如ArrayList中的clear()方法:this

/**
     * Removes all of the elements from this list.  The list will
     * be empty after this call returns.
     */
    public void clear() {
        modCount++;

        // clear to let GC do its work
        for (int i = 0; i < size; i++)
            elementData[i] = null;

        size = 0;
    }复制代码

显式地设置强引用对象为null,或让其超出对象的生命周期范围,则垃圾回收器认为该对象不存在引用,就会回收这个对象。具体何时收集这要取决于具体的垃圾回收器。spa

软引用

若是一个对象只具备软引用(Soft Reference),当内存空间充足时,垃圾回收器就不会回收它;若是内存空间不足了,就会回收这些对象的内存。只要垃圾回收器没有回收它,该对象就能够被程序使用。让咱们来看一个例子具体了解一下:线程

String str = new String("abc");
    SoftReference<String> softReference = new SoftReference<>(str);
    String result = softReference.get();复制代码

让咱们来看一下get()code

public T get() {
        T o = super.get();
        // timestamp表明上一次软引用上一次被使用的时间(初始化、get())
        // clock表明上一次GC的时间
        if (o != null && this.timestamp != clock)
            this.timestamp = clock;
        return o;
    }复制代码

所以,软引用在被垃圾回收时,也遵循LRU法则,优先回收最近最少被使用的对象进行回收。cdn

软引用的使用场景可能是内存敏感的高速缓存。具体来讲,就是咱们但愿将数据存放到缓存中,这样能够快速进行读取。可是,当 JVM 中内存不够用时,咱们又不但愿缓存数据会占用到 JVM 的内存。例如配合ReferenceQueue,若是软引用所引用对象被垃圾回收,JVM 就会把这个软引用加入到与之关联的引用队列中:对象

ReferenceQueue<String> referenceQueue = new ReferenceQueue<>();
    String str = new String("abc");
    SoftReference<String> softReference = new SoftReference<>(str, referenceQueue);

    str = null;
    // Notify GC
    System.gc();

    System.out.println(softReference.get()); // abc

    Reference<? extends String> reference = referenceQueue.poll();
    System.out.println(reference); //null复制代码

可是须要注意的是,若是使用软引用缓存,有可能致使Full GC增多。

弱引用

若是一个对象只具备弱引用(Weak Reference),其生命周期相比于软引用更加短暂。在垃圾回收器线程扫描它所管辖的内存区域的过程当中,一旦发现了只具备弱引用的对象,无论当前内存空间足够与否,都会对它进行回收。不过,因为垃圾回收器是一个优先级很低的线程,所以不必定会很快发现那些只具备弱引用的对象。其使用为:

String str = new String("abc");
    WeakReference<String> weakReference = new WeakReference<>(str);
    str = weakReference.get();复制代码

讲到弱引用,就不得不提到WeakHashMap。和HashMap相比,当咱们给 JVM 分配的内存不足的时候,HashMap 宁肯抛出 OutOfMemoryError 异常,也不会回收其相应的没有被引用的对象,而 WeakHashMap 则会回收存储在其中但有被引用的对象。

WeakHashMap 经过将一些没有被引用的键的值赋值为 null ,这样的话就会告知GC去回收这些存储的值了。假如咱们特意传入 key 为 null 的键,WeakHashMap 会将键设置为特殊的 Oject,源码为:

public V put(K key, V value) {
        // key会被从新赋值
        Object k = maskNull(key);
        int h = hash(k);
        Entry<K,V>[] tab = getTable();
        int i = indexFor(h, tab.length);

        for (Entry<K,V> e = tab[i]; e != null; e = e.next) {
            if (h == e.hash && eq(k, e.get())) {
                V oldValue = e.value;
                if (value != oldValue)
                    e.value = value;
                return oldValue;
            }
        }

        modCount++;
        Entry<K,V> e = tab[i];
        tab[i] = new Entry<>(k, value, queue, h, e);
        if (++size >= threshold)
            resize(tab.length * 2);
        return null;
    }

    /**
     * Value representing null keys inside tables.
     * 特殊的key
     */
    private static final Object NULL_KEY = new Object();

    /**
     * Use NULL_KEY for key if it is null.
     */
    private static Object maskNull(Object key) {
        return (key == null) ? NULL_KEY : key;
    }复制代码

虚引用

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

虚引用主要用来跟踪对象被垃圾回收器回收的活动。 虚引用与软引用和弱引用的一个区别在于:

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

例如:

String str = new String("abc");
    ReferenceQueue queue = new ReferenceQueue();
    // 建立虚引用,要求必须与一个引用队列关联
    PhantomReference pr = new PhantomReference(str, queue);复制代码

程序能够经过判断引用队列中是否已经加入了虚引用,来了解被引用的对象是否将要进行垃圾回收。若是程序发现某个虚引用已经被加入到引用队列,那么就能够在所引用的对象的内存被回收以前采起必要的行动,也能够理解为一种回调方法。

总结

Java 中4种引用的级别和强度由高到低依次为:强引用 -> 软引用 -> 弱引用 -> 虚引用

经过表格,说明其特性:

引用类型 被垃圾回收的时间 使用场景
生存时间
强引用
历来不会
对象的通常状态 JVM中止运行时
软引用
内存不足时
对象缓存
内存不足时
弱引用
正常垃圾回收时
对象缓存
垃圾回收后终止
虚引用
正常垃圾回收时
跟踪对象的垃圾回收 垃圾回收后终止

有兴趣的话能够访问个人博客或者关注个人公众号、头条号,说不定会有意外的惊喜。

death00.github.io/

相关文章
相关标签/搜索