Java ~ Reference ~ WeakReference
创始人
2024-02-03 20:48:24
0

一 概述


    WeakReference(弱引用)类是Reference(引用)类的四大子类之一,只被WeakReference(弱引用)类对象持有的对象被称为弱可达(weakly reachable)对象。在Java中关于弱引用的定义是:弱引用等价于没有引用,其存在无法对对象的生命周期产生任何影响,即对象的诞生、初始化、使用及回收完全与弱引用无关。这不禁会令人产生疑惑…难道弱引用就是无引用吗?那引入所谓的弱引用又有什么意义?这不是脱裤子放屁多次一举吗?

    事实上有这样的疑惑是非常正常的,因为基本上所有的资料都会在有意或无意中将弱引用和无引用画上等号。如此叙述其实并没有错,但很容易令学习者产生一个思维固化,即只将弱引用与无引用进行对比,从而无法真正明晰两者的区别。例如我们无法阐述出一个对象没有引用和只拥有任意数量个弱引用的区别,事实上其在实际运行中也确实没有区别…想要真正明白两者差异需要结合更高等级的引用加以对比,以强引用举例:一个对象拥有一个强引用以及九个弱引用和一个对象拥有十个强引用有区别吗?有!而且非常大!前者只需断开一个强引用即可GC回收,而后者却需要断开所有的十个强引用才可。

    可以看出,所谓的弱引用,乃至软引用,本质上是一种强度可变的引用。当有真实的强引用与之一同存在时,其就是强引用。而一旦失去了共存的强引用,如果说软引用还能保持自身的强度概念(可有可无)的话,那弱引用就直接变为无引用了。而对于无引用…它就是无引用…所以弱引用与无引用的真正区别在于其自身引用强度的可变性

    WeakReference(弱引用)类常用于帮助GC回收。很多资料都会说弱引用适合用作缓存,可但凡思考过其特性的话应该都不会这么想…一个需要配合强引用才能够保证自身存在的对象适合做哪门子缓存?这不就是个常规的强引用对象么?真正适合做缓存的应该是软引用才对。基于可有可无的引用强度,即使是在没有共存强引用的环境下,只要空闲内存充裕,软引用自身也能够保证热点数据持久存在,这与缓存的运用场景是高度契合的。弱引用的作用主要是用于帮助GC回收,就像上文中举例的场景:一个拥有一个强引用以及九个弱引用的对象和一个拥有十个强引用的对象哪个更容易被GC回收?显然是前者,因为其只需要断开一个强引用就可以了。这个特性在实际开发中非常有用,不仅可以处理遗漏/难以/无法断开一些比较隐秘的强引用而使得对象无法被GC回收最终导致OOM的情况,也有助于简化GC回收。

二 源码及机制详解


 类

    WeakReference(弱引用)类继承自Reference(引用)类。

/*** Weak reference objects, which do not prevent their referents from being* made finalizable, finalized, and then reclaimed.  Weak references are most* often used to implement canonicalizing mappings.* 弱引用对象,它不阻止它们的所指对象被设定为可终结、最终完成,然后被回收。弱引用最常用于实现规范化映射。* 

* Suppose that the garbage collector determines at a certain point in time* that an object is weakly* reachable. At that time it will atomically clear all weak references to* that object and all weak references to any other weakly-reachable objects* from which that object is reachable through a chain of strong and soft* references. At the same time it will declare all of the formerly* weakly-reachable objects to be finalizable. At the same time or at some* later time it will enqueue those newly-cleared weak references that are* registered with reference queues.* 假设垃圾收集器在某个时间点确定一个对象是弱可达的。此时,它将自动清除该对象以及可通过强/软引用链访问该对象的弱可达对象的所有弱引用。* 与此同时,它将声明所有以前弱可达对象是可终结的(可回收的)。在同一时间或稍后的某个时间,它将对那些注册在引用队列中的新清除的弱引用进行入队。** @author Mark Reinhold* @since 1.2*/ public class WeakReference extends Reference {... }

 构造方法

    public WeakReference(T referent) —— 只指定所指对象,不注册引用队列。可知,WeakReference(弱引用)类可不搭配引用队列使用。

/*** Creates a new weak reference that refers to the given object.  The new* reference is not registered with any queue.** @param referent object the new weak reference will refer to*/
public WeakReference(T referent) {super(referent);
}

    public WeakReference(T referent, ReferenceQueue q) —— 指定所指对象,并注册引用队列。

/*** Creates a new weak reference that refers to the given object and is* registered with the given queue.** @param referent object the new weak reference will refer to* @param q        the queue with which the reference is to be registered,*                 or null if registration is not required*/
public WeakReference(T referent, ReferenceQueue q) {super(referent, q);
}

三 使用案例


 WeakHashMap(弱哈希映射)类

    WeakHashMap(弱哈希映射)类与HashMap(哈希映射)类同为Map(映射)类的主流实现之一,且两者作用相似。与HashMap(哈希映射)类不同的是,WeakHashMap(弱哈希映射)类的节点类Entry(入口)类是直接继承于WeakReference(弱引用)类,因此其本身也是一个弱引用。Entry(入口)类将Reference(引用)类的所指对象作为键来使用,这就使得一旦键对象断开了WeakHashMap(弱哈希映射)类外部的强引用,就会被GC回收,并将其对应的WeakReference(弱引用)类对象,也就是Entry(入口)类对象加入到引用队列中。

    WeakHashMap(弱哈希映射)类之所以如此设计是为了节省内存。在某些情况下键值对只会短期使用,但映射却往往是长时间存在的,如果是强引用的话就必须手动的删除,但实际开发中开发者往往会以整个映射为单位进行操作,这就使得映射中可能会存在大量的无用键值对(即不会再使用又无法被GC回收)占用内存。而如果使用弱引用,只要键对象断开了WeakHashMap(弱哈希映射)类外部的强引用,那其就会被GC回收,且对应的Entry(入口)类对象(键值对)也会加入到引用队列中,那就可以在下次调用WeakHashMap(弱哈希映射)类方法的同时触发删除掉这些引用队列中已经无用的Entry(入口)类对象(键值对)以节省内存。

/*** Reference queue for cleared WeakEntries* 引用队列,用于存放键对象已经被GC回收的Entry类对象,以便将之用节点数组中删除。*/
private final ReferenceQueue queue = new ReferenceQueue<>();/*** The entries in this hash table extend WeakReference, using its main ref* field as the key.*/
private static class Entry extends WeakReference implements Map.Entry {V value;final int hash;Entry next;...
}
 

 ThreadLocalMap(线程本地映射)类

    ThreadLocalMap(线程本地映射)类是ThreadLocal(线程本地)类中自我内部实现的一个映射,需要注意的是其不是Map(映射)类的子类。ThreadLocalMap(线程本地映射)类与WeakHashMap(弱哈希映射)类一样都把节点类Entry(入口)类直接继承于WeakReference(弱引用)类,那同理在键对象的GC回收商也是相同机制,只是其没有搭配引用队列使用,因此就没有Entry(入口)类对象(键值对)加入到引用队列中的操作。

    ThreadLocalMap(线程本地映射)类如此设计是为了保证ThreadLocal(线程本地)类对象能够正常的被GC回收。众所周知ThreadLocalMap(线程本地映射)类是以ThreadLocal(线程本地)类对象为键对象的,而ThreadLocalMap(线程本地映射)类对象又各线程人手一份,这就代表着一个ThreadLocal(线程本地)类对象可能同时被成千上万个ThreadLocalMap(线程本地映射)类对象的Entry(入口)类对象持有引用。如果这些引用都是强引用的话,那仅仅在全局上断开ThreadLocal(线程本地)类对象的强引用显然是不够的,还需要每个线程全部断开才能使ThreadLocal(线程本地)类对象正常的被GC回收。但如果设计为弱引用的话,那一但全局的强引用被断开,那其余所有的弱引用都会等价于无引用,ThreadLocal(线程本地)类对象自然也就能够顺利的GC回收了。

/*** The entries in this hash map extend WeakReference, using* its main ref field as the key (which is always a* ThreadLocal object).  Note that null keys (i.e. entry.get()* == null) mean that the key is no longer referenced, so the* entry can be expunged from table.  Such entries are referred to* as "stale entries" in the code that follows.*/
static class Entry extends WeakReference> {/*** The value associated with this ThreadLocal.*/Object value;Entry(ThreadLocal k, Object v) {super(k);value = v;}
}

四 相关系列


  • 《Java ~ Reference》
  • 《Java ~ Reference ~ ReferenceQueue》
  • 《Java ~ Reference ~ SoftReference》
  • 《Java ~ Reference ~ WeakReference》
  • 《Java ~ Reference ~ PhantomReference/Cleaner》
  • 《Java ~ Reference ~ FinalReference/Finalizer》

相关内容

热门资讯

AWSECS:访问外部网络时出... 如果您在AWS ECS中部署了应用程序,并且该应用程序需要访问外部网络,但是无法正常访问,可能是因为...
AWSElasticBeans... 在Dockerfile中手动配置nginx反向代理。例如,在Dockerfile中添加以下代码:FR...
AWR报告解读 WORKLOAD REPOSITORY PDB report (PDB snapshots) AW...
AWS管理控制台菜单和权限 要在AWS管理控制台中创建菜单和权限,您可以使用AWS Identity and Access Ma...
银河麒麟V10SP1高级服务器... 银河麒麟高级服务器操作系统简介: 银河麒麟高级服务器操作系统V10是针对企业级关键业务...
北信源内网安全管理卸载 北信源内网安全管理是一款网络安全管理软件,主要用于保护内网安全。在日常使用过程中,卸载该软件是一种常...
群晖外网访问终极解决方法:IP... 写在前面的话 受够了群晖的quickconnet的小水管了,急需一个新的解决方法&#x...
​ToDesk 远程工具安装及... 目录 前言 ToDesk 优势 ToDesk 下载安装 ToDesk 功能展示 文件传输 设备链接 ...
Azure构建流程(Power... 这可能是由于配置错误导致的问题。请检查构建流程任务中的“发布构建制品”步骤,确保正确配置了“Arti...
不能访问光猫的的管理页面 光猫是现代家庭宽带网络的重要组成部分,它可以提供高速稳定的网络连接。但是,有时候我们会遇到不能访问光...