系列九、Entry的key为什么要设计成弱引用

文章解释了Java中ThreadLocal的key设计为弱引用的原因,避免强引用可能导致的内存泄漏问题。即使忘记移除,弱引用在ThreadLocal不再使用后能确保其自动回收,从而保护value不致内存泄漏。

一、Entry的key为什么要设计成弱引用

1.1、四大引用类型

Java中的四种引用

1.2、Entry源码

1.3、为什么设计为弱引用

1.3.1、官网

To help deal with very large and long-lived usages, the hash table entries use WeakReferences for keys。

1.3.2、ThreadLocal引用示意图

再说ThreadLocal的key设计为弱引用之前,先来说说如果设计为强引用,会有什么问题?

key使用强引用:假设在业务代码中使用完ThreadLocal,ThreadLocal中的ref被回收了,但是因为ThreadLocalMap的Entry的key强引用了ThreadLocal,造成ThreadLocal无法回收,在没    有删除这个Entry以及当前线程依然在运行的前提下,始终有一条强引用链,即:Thread ref===>Thread obj===>ThreadLocalMap obj===>Entry,Entry就不会被回收(Entry中包括ThreadLocal实例和value),导致Entry内存泄漏;

key使用弱引用:同样假设在业务代码中使用完ThreadLocal,ThreadLocal ref被回收了,由于ThreadLocalMap的Entry的key使用的是弱引用,当gc时ThreadLocal obj能够顺利被回收,此时Entry中的key为null,但是在没有手动删除这个Entry以及当前线程依然在运行的前提下,也始终存在着一条强引用链,即:Thread ref===>Thread obj===>ThreadLocalMap obj===>Entry===>value,value不会被回收,而这块value永远不会被访问到了,导致value内存泄漏;

总结:既然key设计为强引用和弱引用都有可能出现内存泄漏的问题,那么为什么要设计为弱引用呢?事实上,在ThreadLocalMap中的set()、getEntry() 方法中,会对key是否为null(key为ThreadLocal)进行判断,如果key为null的话,那么会对value设置为null,这就意味着使用完ThreadLocal,当前线程依然运行的前提下,就算忘记调用remove()方法了,弱引用也比强引用多一层保障,所引用的key(ThreadLocal)会被回收,对应的value会在下次ThreadLocalMap中调用set()、getEntry()、remove()中的任一方法时都会被清除,从而避免内存泄露。

基于数据驱动的 Koopman 算子的递归神经网络模型线性化,用于纳米定位系统的预测控制研究(Matlab代码实现)内容概要:本文围绕“基于数据驱动的Koopman算子的递归神经网络模型线性化”展开,旨在研究纳米定位系统的预测控制问题,并提供完整的Matlab代码实现。文章结合数据驱动方法与Koopman算子理论,利用递归神经网络(RNN)对非线性系统进行建模与线性化处理,从而提升纳米级定位系统的精度与动态响应性能。该方法通过提取系统隐含动态特征,构建近似线性模型,便于后续模型预测控制(MPC)的设计与优化,适用于高精度自动化控制场景。文中还展示了相关实验验证与仿真结果,证明了该方法的有效性和先进性。; 适合人群:具备一定控制理论基础和Matlab编程能力,从事精密控制、智能制造、自动化或相关领域研究的研究生、科研人员及工程技术人员。; 使用场景及目标:①应用于纳米级精密定位系统(如原子力显微镜、半导体制造设备)中的高性能控制设计;②为非线性系统建模与线性化提供一种结合深度学习与现代控制理论的新思路;③帮助读者掌握Koopman算子、RNN建模与模型预测控制的综合应用。; 阅读建议:建议读者结合提供的Matlab代码逐段理解算法实现流程,重点关注数据预处理、RNN结构设计、Koopman观测矩阵构建及MPC控制器集等关键环节,并可通过更换实际系统数据进行迁移验证,深化对方法泛化能力的理解。
### ThreadLocalKey 设计弱引用的原因 在 Java 的 `ThreadLocal` 实现中,`ThreadLocalMap` 是每个线程内部维护的数据结构,用于存储与当前线程绑定的变量副本。`ThreadLocalMap` 中的键(Key)是 `ThreadLocal` 对象本身,而(Value)则是用户存储的数据。为了优化内存管理和避免潜在的内存泄漏问题,`ThreadLocal` 的键被设计弱引用(WeakReference)。 #### 弱引用的作用 弱引用的主要作用是允许垃圾回收器在没有其他强引用的情况下回收对象。对于 `ThreadLocal` 而言,将键设置为弱引用可以确保即使外部不再持有对 `ThreadLocal` 实例的强引用,只要该实例没有被其他地方引用,它仍然可以被垃圾回收器回收。 具体来说,如果 `ThreadLocal` 的键使用的是强引用,那么即使应用程序代码中已经不再需要某个 `ThreadLocal` 实例,并将其置为 `null`,但由于线程未终止且其 `ThreadLocalMap` 仍然持有对该 `ThreadLocal` 的引用,这会导致该 `ThreadLocal` 实例无法被垃圾回收。这种情况在使用线程池时尤为明显,因为线程会被复用,导致这些无用的 `ThreadLocal` 实例长期驻留内存中,造内存泄漏风险[^1]。 通过使用弱引用作为键,当一个 `ThreadLocal` 实例的所有外部强引用都被释放后,垃圾回收器可以在下一次清理时回收这个 `ThreadLocal` 实例。尽管如此,由于 `ThreadLocalMap` 中的仍然是强引用,因此需要额外的机制来清理那些已经被回收的 `ThreadLocal` 键所对应的条目,以防止对象无法被回收的问题[^4]。 #### 内存模型和实现原理 每个线程都有自己的 `ThreadLocalMap` 实例,这是通过 `Thread` 类中的私有字段实现的。每当调用 `ThreadLocal.set()` 或 `ThreadLocal.get()` 方法时,都会访问当前线程的 `ThreadLocalMap` 并进行相应的操作。这种设计保证了不同线程之间的数据隔离性,同时也使得每个线程能够独立地修改自己的本地变量而不影响其他线程的状态[^3]。 ```java // Entry 定义示例 static class Entry extends WeakReference<ThreadLocal<?>> { Object value; Entry(ThreadLocal<?> k, Object v) { super(k); // key (ThreadLocal) 被弱引用 value = v; // value 是强引用 } } ``` 上述代码展示了 `Entry` 类是如何继承自 `WeakReference` 来创建一个带有弱引用键的映射条目。这样做的目的是为了让 `ThreadLocal` 对象能够在不再被使用时及时被垃圾回收,从而减少不必要的内存占用。 #### 相关问题 - 如何正确地清理 ThreadLocal 中的以避免内存泄漏? - 在什么情况下 ThreadLocal 可能会导致内存泄漏? - ThreadLocalMap 是如何处理哈希冲突的?
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值