threadlocal内存泄露_深入理解JVMThreadLocal内存泄露问题本质分析与代码编写最佳实践...

ThreadLocal内存泄露问题本质分析:

接着ThreadLocal继续探究,在上一次深入理解JVM-ThreadLocal流程深入分析ThreadLocal对于WeakReference的使用分析已经对于它里面的Entry要用WeakReference意义进行了分析,这一次再对ThreadLocal的底层使用WeakReference的来解决内存泄漏问题进行一个本质的探讨,虽说在上一次已经阐述得也比较清楚了,但是这块的细节还可以再扣一下,细节扣得越细对于你了解ThreadLocal的原理也就越加的深刻明了,不管是在未来的面试还是工作当中这种所谓的“啰嗦”都是有利无害的。

这里先来回顾一下使用ThreadLocal的基本写法:

package com.jvm.reference;public class MyTest6 {    private static final ThreadLocal THREADLOCAL = new ThreadLocal<>();    public static void main(String[] args) {        THREADLOCAL.set("hello");        new Thread(() -> {            THREADLOCAL.set("thread1");            System.out.println("thread1:" + THREADLOCAL.get());        }).start();        new Thread(() -> {            THREADLOCAL.set("thread2");            System.out.println("thread2:" + THREADLOCAL.get());        }).start();        System.out.println(THREADLOCAL.get());    }}

下面用图的方式一步步来阐述ThreadLocal内存泄漏的问题,如之前篇章所了解的,从Thread类开始,因为它里面有个它:

338c046ac049f2bf6c0aa1cdf14278ed.png

既然是阐述跟内存相关的问题,当然得从内存的结构开始,也就是栈与堆:

534cdf32a326a01e55557128fc34f4a0.png

此时由于咱们new了一个线程:

c61a8fbd1155d201f7e4d91521ff4faf.png

所以内存此时的分布为:

75f525d6ef32798cfb55c80cc7040aa1.png

而我们又new了一个ThreadLocal对象了:

f235e5ecf297185eeb2a0ac9a5685d59.png

所以内存分布变化为:

ad32b8cb8c8fcb141d9cf622804e2ea0.png

那问一下,此时堆中存在的两个对象是怎么关联起来的呢?

7580171b2549eb06bb43299c1b997428.png

继续往下梳理,对于Thread类中它有一个ThreadLocalMap成员变量,所以此时内存为:

603bdf13ab8cd07cb9bd5341eb03d805.png

而ThreadLocalMap它里面其实有一个table的成员变量,它是一个Entry数组类型的,如下:

869257deb041d1109c0a2f633df65698.png

此时图又变为:

2976042f634d19b5ca71a458b79d6dde.png

其中每一个Entry它是一个虚引用:

21b9cf32fc87cc7b22c1b6a9bc6e6e68.png

那这个弱引用里指向的是哪个对象呢?

3a57ca6535f238eb32e6379923f1f9cd.png

也就是此时的k是引用的ThreadLocal对象了,内存图此时为:

2db2105e9ad774220a9fb9eaf7c8f325.png

那接下来重点就是来看一下关于ThreadLocal内存泄漏的问题,当然ThreadLocal本身是已经解决了内存泄漏点了,只是我们带着学术的角度来理解JDK它为啥要这样设计,这才有助于我们的成长,如上一篇也已经分析过了重点是这个Entry是继承了一个弱引用是解决内存泄漏的根本,那要分析这个泄漏点,那反过来分析一下,假如此时Entry是强引用ThreadLocal此时的情况又会怎么样,如下:

34e0d792415155c562f5b7717c300f9c.png

假如咱们ThreadLocal需要加收,肯定栈中的引用需要断掉,如下:

dae8b6b42e5cb1363afd91a75f20253b.png

那照理堆中的它得被垃圾收集器给回收:

e1b9308b3254d255798686be4d5ec111.png

但是!!!此时从图中可以看到它有被其它对象强引用的,也就是ThreadLocalMap当中的Entry数组中的key所强引用着的,那造成的影响就是这个ThreadLocal对象永远也得不到回收,它得不到回收,那么这个Entry数组的元素永远会持续的只增不减,而糟糕的是此时由于栈中已经没有引用指向该ThreadLocal对象了,那么这处毫无意义的对象会永远在堆中占据着空间得不到释放,那不就是典型内存泄漏发生了么?要想解决这种问题,必须采用弱引用了:

db7e487349a58963de3d63d2393b0773.png

所以接下来再从这种弱引用的角度来看一下,此时的内存泄漏是否还会存在?还是栈中的ThreadLocal引用会断掉:

5d6cbdfd2efaed9b005e868e3024c8d5.png

而此时堆中的ThreadLocal对象只被一个Entry为key的弱引用所指向了,而根据弱引用回收的特点:

c99b9c159d39d8322d10369a0e13d167.png

此时它就会被垃圾回收器所回收掉,也就是:

a0543ad1494687aee2b44e6714af5256.png

是不是一切都完美无内存泄漏了呢?这点可能是比较细的一个点,但是又是考虑你对ThreadLocal理解是否透的一个关键点,就是此时有Key它原来指向的ThreadLocal如今就变为:

6129d55f80f0ba7537cf2d6bb415e79f.png

新的问题又出现了!!!既然key为null了,那。。

1d1eac8a5b422e695f786e311539e860.png

那。。将Value也定义成WeakReference不就可以解决这个新的内存泄漏的情况了呢?显示这种是不行的,因为弱引用是随时有可能被回收的,那不我们有时获取的值不就不确定了么?那对于这个Value的内存泄漏Java的设计者已经帮我们考虑到了,也就是如上篇所分析过的,在ThreadLocal的set和get方法中都会有一个检查key为null的逻辑,专门就是来处理这种泄漏情况下,下面再翻翻源码看一看:

先看一下set()方法:

b1199cc65e411ad0ff957fc5b9e9cd93.png

a6bc3882d7280752c781279056d67019.png

481c6795221ca703fe72361f51507c63.png

be843f295ee77653be35a89a7971dba9.png

而接下来看一下get()方法也有类似的处理逻辑:

73c0235fc3585e2bb42e3c720cbebfd7.png

c400571717660c99d400185ac0024d74.png

e06d2367f56808a4a9ed4b922ba29fed.png

还有一个方法remove,也有:

334d54968bccd36fb2bb847caefed1dc.png 

2dc5d8f20e90bbbb7423a4648dfec526.png

代码编写最佳实践:

其实吧这是一个非常小的点,但是这一点是看你对ThreadLocal了解程序的一个体现细节,就是我们当发现ThreadLocal如果不用的话,最好要调用这么一个方法,啥方法呢?如下:

635b43d952b15fa8481cd0954a649c27.png

调用它意味着啥呢?其实意味着:

fad51786c6cb02968e6ecec49bb20366.png

看一下它的源码:

d964a31d70021766de9b7823c4286960.png 

7c4185caa7dd223f0eaedd3443f286ae.png

这样代码的写法在实际中是比较容易忽略的,但是如果在实际中能考虑到这点,而且能说明为啥要这么用,那证明对于ThreadLocal了解也比较透了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值