聊一聊ThreadLocal内存泄漏的问题

回答任何一个问题的时候应该要遵循:明确题意-->深入浅出-->举例说明-->总结,这四个步骤很重要,可以让你沉着冷静,思路清晰,避免尴尬。

01 —  明确题意

明确题意的意思就是先明确一下面试官的题目,能避免自己理解有误而跑题,可以这样说:ThreadLocal内存泄漏的原因主要是因为ThreadLocal中包含了ThreadLocalMap,然而ThreadLocalMap的对象是在Thread中的,如果Thread没有结束,则ThreadLocalMap一直不会释放,假如ThreadLocalMap中设置了很多值,而且没有手动设置remove(),则可能会造成内存泄露。网上有些网友的答案是因为ThreadLocal作为一个弱引用的key然后造成每次GC的时候会回收掉ThreadLocal,导致无法访问value,然后造成了内存泄漏,这是完全错误的,其实和弱引用没有关系。

02 — 深入浅出

先思考一下,如果每次GC都把ThreadLocal给回收掉,那么业务代码在运行的时候出现GC,ThreadLocal被回收之后,业务代码就会获取不到值,这样就会出现问题。弱引用的定义确实是在GC的时候就会被回收掉,如果ThreadLocal又被其它强引用指向了,则它是不会被回收的,想想我们在使用ThreadLocal的时候,我们是怎么定义并使用他的呢?

可以看到threadLocal可以定义为一个全局的静态变量或者是一个局部变量,threadLocal是强引用,不管怎么样它都不会被回收掉,只是又将它的引用又给到了ThreadLocalMap中的Entry.key中,虽然这个key是弱引用,但是ThreadLocal对象是不会被回收的。

如果一个线程Thread结束了,那么Thread里面的threadLocals将会被回收掉,也就是ThreadLocalMap数据结构会被回收掉,也就不会出现内存泄漏的问题。那出现内存泄露的情况是什么呢?

当Thread一直没有结束时,Thread中的threadLocals就不会被回收,threadLocals里面存储的Entry如果不手动删除的话,就会一直存在这个threadLocals里面,所以就会出现内存泄漏的问题,通常在线程池的情况下,一个Thread会使用很长时间,如果在使用的过程中,一直向里面设置Entry,也就是key = ThreadLocal 和 value=业务对象,如果一个线程的任务执行完毕之后,没有手动设置remove()方法释放掉这个Entry的话,那么Thread的threadLocals中的Entry将会一直膨胀,一直停留在Thread中的threadLocals中,造成内存泄漏,所以一定要手动设置remove()。

那这会延伸出来另外一个问题,为什么使用弱引用?

弱引用的作用就是当出现GC的时候会回收这些弱引用的对象,如果有些业务不是定义的全局的静态变量而是局部的变量,例如:通过ThreadLocal保存一些在整个线程中全局都可以使用的变量,减少方法与方法调用的参数传递。此时当一个任务执行完成之后,可以将ThreadLocal设置成为null,局部变量的强引用就会失效,存在Map中的Entry的key只有弱引用,如果不进行清理的话,则会出现内存泄漏的问题,此时出现GC的时候就会回收掉ThreadLocal对象,也就是说ThreadLocal是尽量的去避免内存泄漏的问题。但是解决不了根本的问题,所以需要开发人员手动去调用remove()方法才能够彻底解决内存泄漏的问题。

03 — 举例说明

找到了一个开源项目ruoyi框架的源码,可以看到ThreadLocal是一个全局的静态变量,不同的线程不会影响彼此,ThreadLocal永远不会被回收掉,在线程池的情况下,当我们操作完之后,需要手动调用remove()方法,上图也提供了clearDataSourceType()方法用来清除任务执行完之后的Entry对象。

可以看到在Aspect切面中的around方法中,最终调用了此方法。

04 — 总结

最后来句总结:这就是我对ThreadLocal内存泄漏的理解。主要的作用是提醒面试官说完了,看看面试官是否有问题要问,或者让面试官提问下一个问题,不要让面试官或者自己尴尬。

                   

  • 16
    点赞
  • 63
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 16
    评论
ThreadLocal 内存泄露问题是指当使用ThreadLocal类时,如果没有正确地进行清理和处理,就有可能导致内存泄露的情况发生。这是因为ThreadLocal对象的生命周期与线程的生命周期相对独立,当线程结束时,ThreadLocal对象没有被垃圾回收,且其中存储的数据也无法被访问,从而导致内存泄露。 具体来说,ThreadLocal类通过操作ThreadLocalMap来存储每个线程的数据。当一个线程结束时,如果没有正确地清理ThreadLocal对象,那么ThreadLocalMap中与该线程相关的条目将无法被删除。这意味着,即使这些条目对应的线程不再活跃,它们却仍然占据着内存空间。 一种常见的导致ThreadLocal内存泄露的情况是在使用完ThreadLocal对象后未调用其remove方法进行清理操作。如果在一个长时间运行的线程中重复使用ThreadLocal对象,而不进行清理操作,就导致ThreadLocalMap中的条目越来越多,从而造成内存泄露。 另外,当ThreadLocal对象被作为静态变量使用时,也容易出现内存泄露的问题。因为静态变量的生命周期很长,如果静态ThreadLocal对象没有被妥善处理,那么其中的数据也将无法被释放。 为了避免ThreadLocal内存泄露,应该养成良好的编程习惯,确保在使用完ThreadLocal对象后,及时调用其remove方法进行清理。另外,如果ThreadLocal对象被用作静态变量,也应该在不再使用时手动将其置为null,以便让垃圾回收器能够回收相关的内存空间。 参考资料: :可以发现问题ThreadLocal已经被清理掉了,代表现在已经没有方式去访问当前ThreadLocal存到Map里的value数据了 。 :ThreadLocal就相当于一个访问工具类,通过操作ThreadLocal对象的方法 来操作存储在当前线程内部的ThreadLocalMap里的值 。 :一篇文章我们来分析一个Java中ThreadLocal内存泄露的案例。分析问题的过程比结果更重要,理论结合实际才能彻底分析出内存泄漏的原因。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 16
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

山竹之七语

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值