WeakReference(弱引用)属于Java四大引用:强引用、软引用、弱引用、虚引用的一员,下面会详解WeakReference@mikechen
Java引用简要介绍
在详解介绍WeakReference(弱引用)前,我们稍微简短回顾下Java的四大引用的其它三个引用类型。
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面试题总结》,后台回复【架构】,即可获取《阿里架构师进阶专题全部合集》