WeakReference(Java弱引用)使用与原理详解

WeakReference(Java弱引用)使用与原理详解-mikechen

WeakReference(弱引用)属于Java四大引用:强引用、软引用、弱引用、虚引用的一员,下面会详解WeakReference@mikechen

Java引用简要介绍

在详解介绍WeakReference(弱引用)前,我们稍微简短回顾下Java的四大引用的其它三个引用类型。

WeakReference(Java弱引用)使用与原理详解-mikechen

1.强引用(Strong Reference)

通常我们通过new来创建一个新对象时返回的引用就是一个强引用,如果一个对象存在强引用,它是不会被 GC 回收的。

2.软引用(Soft Reference)

软引用和弱引用的区别在于,软引用:如果内存不足,则回收,弱引用:不管内存是否充足,回收。

3.虚引用(Phantom Reference)

虚引用是Java中最弱的引用,那么它弱到什么程度呢?它是如此脆弱以至于我们通过虚引用甚至无法获取到被引用的对象,虚引用存在的唯一作用就是当它指向的对象被回收后,虚引用本身会被加入到引用队列中,用作记录它指向的对象已被回收。

 

什么是WeakReference弱引用

WeakReference翻译过来是弱引用,当弱引用的对象拥有更短暂的生命周期,在垃圾回收器线程扫描它所管辖的内存区域的过程中,一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存。

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

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

 

为什么需要WeakReference弱引用

Java常通过使用弱引用来避免内存泄漏,例如在JDK中有一种内存变量ThreadLocal,通过ThreadLocal变量可以使共享的变量在不同的线程中有不同的副本,原理是在每一个Thread有一个threadLocalMap的属性,用来存放ThreadLocal对象,ThreadLocalMap中是通过一个Entry[]的散列表存放ThreadLocal变量以及ThreadLocal的value,而作为Entry的key的ThreadLocal就是使用的弱引用,结构如下:

static class Entry extends WeakReference<ThreadLocal<?>> {
    /** The value associated with this ThreadLocal. */
    Object value;

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

Entry通过继承了WeakReference并通过get、set设置ThreadLocal为Entry的referent。

这里为什么要使用弱引用呢?

原因是如果不使用弱引用,那么当持有value的强引用释放掉后,当线程没有回收释放时,threadLocalMap会一直持有ThreadLocal以及value的强应用,导致value不能够被回收,从而造成内存泄漏。

通过使用弱引用,当ThreadLocal的强引用释放掉后,通过一次系统gc检查,发现ThreadLocal对象只有threadLocalMap中Entry的若引用持有,此时根据弱引用的机制就会回收ThreadLocal对象,从而避免了内存泄露。

 

WeakReference类结构

WeakReference继承Reference,其中只有两个构造函数:

public class WeakReference<T> extends Reference<T> {
    public WeakReference(T referent) {
        super(referent);
    }

    public WeakReference(T referent, ReferenceQueue<? super T> q) {
        super(referent, q);
    }

}
  • 构造函数(1)的参数referent就是弱引用指向的对象(弱引用和被弱引用指向的对象是两个不同的概念);
  • 构造函数(2)的参数比(1)多了一个ReferenceQueue,在对象被回收后会把弱引用对象,也就是WeakReference对象或者其子类的对象放入队列ReferenceQueue中。

 

WeakReference使用示例

package com.mikechen.java.refenence;

import java.lang.ref.WeakReference;

/**
 * WeakReference使用示例
 *
 * @author mikechen
 */
public class TestWeakReference {
    public static void main(String[] args) throws Exception{

        UserInfo userInfo = new UserInfo("mikechen");
        UserInfo anotherUser = userInfo;
        WeakReference<UserInfo> weakUser = new WeakReference<>(userInfo);

        System.out.println("\nBefore userInfo is null");
        System.out.println("strong ref:" + anotherUser);
        System.out.println("weak ref:" + weakUser.get());

        userInfo = null;
        System.gc();

        System.out.println("\nAfter userInfo is null");
        System.out.println("strong ref:" + anotherUser);
        System.out.println("weak ref:" + weakUser.get());

        anotherUser = new UserInfo("adashui");
        System.gc();
        System.out.println("\nAfter anotherUser is changed");
        System.out.println("strong ref:" + anotherUser);
        System.out.println("weak ref:" + weakUser.get());

    }
}

class UserInfo {
    private String name;

    public UserInfo(String name){
        this.name = name;
    }

    @Override
    public String toString() {
        return "Name is " + name;
    }
}

输出结果为:

weak ref:Name is mikechen

After userInfo is null
strong ref:Name is mikechen
weak ref:Name is mikechen

After anotherUser is changed
strong ref:Name is adashui
weak ref:null

从上面的结果可以看到,刚开始,这三个引用都指向同一个用户对象mikechen, 然后我们将原引用置为null,进行垃圾回收,但由于还有一个强引用指向这个mikechen 所以这个用户对象不会被回收,最后,当没有强引用再指向它了,再做垃圾回收,则mikechen被回收了。

这个结果也印证了WeakReference的作用,如果其指向的对象没有被任何强引用指向,则该对象是可以回收的。

 

 

陈睿mikechen

10年+大厂架构经验,资深技术专家,就职于阿里巴巴、淘宝、百度等一线互联网大厂。

关注「mikechen」公众号,获取更多技术干货!

后台回复面试即可获取《史上最全阿里Java面试题总结》,后台回复架构,即可获取《阿里架构师进阶专题全部合集

评论交流
    说说你的看法