threadLocal之内存泄露

内存泄露

内存泄露为程序在申请内存后,无法释放已申请的内存空间,一次内存泄露危害可以忽略,但内存泄露堆积后果很严重,无论多少内存,迟早会被占光,

广义并通俗的说,就是:不再会被使用的对象或者变量占用的内存不能被回收,就是内存泄露。

强引用与弱引用

强引用,使用最普遍的引用,一个对象具有强引用,不会被垃圾回收器回收。当内存空间不足,Java虚拟机宁愿抛出OutOfMemoryError错误,使程序异常终止,也不回收这种对象。

如果想取消强引用和某个对象之间的关联,可以显式地将引用赋值为null,这样可以使JVM在合适的时间就会回收该对象。

弱引用,JVM进行垃圾回收时,无论内存是否充足,都会回收被弱引用关联的对象。在java中,用java.lang.ref.WeakReference类来表示。可以在缓存中使用弱引用。

 软引用:只有在内存不足的时候JVM才会回收该对象 。

虚引用:就是形同虚设,如果一个对象仅持有虚引用,那么它相当于没有引用,在任何时候都可能被垃圾回收器回收。

  虚引用必须和引用队列关联使用

GC回收机制-如何找到需要回收的对象

JVM如何找到需要回收的对象,方式有两种:

  • 引用计数法:每个对象有一个引用计数属性,新增一个引用时计数加1,引用释放时计数减1,计数为0时可以回收,
  • 可达性分析法:从 GC Roots 开始向下搜索,搜索所走过的路径称为引用链。当一个对象到 GC Roots 没有任何引用链相连时,则证明此对象是不可用的,那么虚拟机就判断是可回收对象。

引用计数法,可能会出现A 引用了 B,B 又引用了 A,这时候就算他们都不再使用了,但因为相互引用 计数器=1 永远无法被回收。

什么是threadLocal?

threadeLocal只是去操作每个线程中的ThreadLocalMap数据结构的一个工具。

每个线程中都有一个ThreadLocalMap数据结构,当执行set方法时,其值是保存在当前线程的threadLocals变量中,当执行set方法中,是从当前线程的threadLocals变量获取。

什么是ThreadLoalMap?

本文分析的是1.7的源码。

从名字上看,可以猜到它也是一个类似HashMap的数据结构,但是在ThreadLocal中,并没实现Map接口。

在ThreadLoalMap中,也是初始化一个大小16的Entry数组,Entry对象用来保存每一个key-value键值对,只不过这里的key永远都是ThreadLocal对象,是不是很神奇,通过ThreadLocal对象的set方法,结果把ThreadLocal对象自己当做key,放进了ThreadLoalMap中。

这里需要注意的是,ThreadLoalMap的Entry是继承WeakReference,和HashMap很大的区别是,Entry中没有next字段,所以就不存在链表的情况了。

为什么会造成内存泄露?

ThreadLocal在的JVM内存情况

image.png

从两个角度分析内存泄漏

先假设K的引用是强引用

  • 当程序运行到某一时刻,需要销毁ThreadLocal对象,它先会销毁栈中的ThreadLoacl的引用。
  • 由于堆中的Entry数组的下标K是ThreadLocal的强引用,这就导致虽然栈中的引用销毁了,但是堆中的没有被销毁,且无法销毁。这就进一步导致了。
  • Entry数组里的内容无法清除,如果重复多次使用同一个Entry数组,就导致上述情况累积发生,然后数组爆满,造成内存泄漏。
  • 即 K和V 存进去就无法清除,累积后数组爆满,内存泄漏。

image.png

既然强引用不好用,就用其它引用

实际情况K的引用是弱引用

单独一个弱引用,也无法解决内存泄漏

  • 当程序运行到某一时刻,需要销毁ThreadLocal对象,它先会销毁栈中的ThreadLoacl的引用。
  • 栈中的ThreadLocal强引用被销毁,由于堆中的Entry数组的下标K是ThreadLocal的弱引用,在下一次垃圾会回收时,因堆中的ThreadLocal没有被强引用,遂会被销毁。至此
  • K无法被消除的情况解决了。但是
  • V却没有删除,就导致了Entry中空有V却没有对应的数组下标与其对应,如此下去,就会导致数组会有很多的V没有对应的K,V的数量增加下去,导致Entry数组爆满,内存泄漏。

image.png

弱引用没有彻底解决内存泄漏咋整?

在代码层面进行一番操作即可

  • jdk源代码

在调用ThreadLocal的get、set等方法时,都会对Entry数组没有K的Value进行清除,因为弱引用会清除Entry数组的K,但不会清除的Value,只要清除了Value就解决了内存泄漏。

image.png

在ThreadLocal的实际应用中应该使用下面的形状,来防止内存泄漏

public class ThreadLocalT {
    private static final ThreadLocal<String> threadLocal = new ThreadLocal<>();//维护的变量类型是String
        try{
            ......
            ......
    }finally {
        threadLocal.remove();//很关键,防止内存泄漏
    }
}
复制代码
  • 因为弱引用需要移除强引用的基础上,才能被垃圾回收
  • 调用remove方法就是移除栈中的强引用。才能确保不会发生内存泄漏。

小结

  • ThreadLocal不是真正存储变量的,ThreadLocalMap中Entry数组才是。
  • ThreadLocalMap是Thread类中的成员变量。
  • 调用ThreadLocal的get、set和remove方法会清除Entry数组中没有K的Value。

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值