threadlocal内存泄露_ThreadLocal内存泄漏问题

ThreadLocal内存泄漏问题

一、概述

ThreadLocal类用来提供线程内部的局部变量。这些变量在多线程环境下访问(通过get或set方法访问)时能保证各个线程里的变量相对独立于其他线程内的变量,ThreadLocal实例通常来说都是private static类型。 总结:ThreadLocal不是为了解决多线程访问共享变量,而是为每个线程创建一个单独的变量副本,提供了保持对象的方法和避免参数传递的复杂性。

ThreadLocal的主要应用场景为按线程多实例(每个线程对应一个实例)的对象的访问,并且这个对象很多地方都要用到。例如:同一个网站登录用户,每个用户服务器会为其开一个线程,每个线程中创建一个ThreadLocal,里面存用户基本信息等,在很多页面跳转时,会显示用户信息或者得到用户的一些信息等频繁操作,这样多线程之间并没有联系而且当前线程也可以及时获取想要的数据。

二、原理

ThreadLocal可以看做是一个容器,容器里面存放着属于当前线程的变量。ThreadLocal类提供了四个对外开放的接口方法,这也是用户操作ThreadLocal类的基本方法:

(1) void set(Object value)设置当前线程的线程局部变量的值。

(2) public Object get()该方法返回当前线程所对应的线程局部变量。

(3) public void remove()将当前线程局部变量的值删除,目的是为了减少内存的占用,该方法是JDK 5.0新增的方法。需要指出的是,当线程结束后,对应该线程的局部变量将自动被垃圾回收,所以显式调用该方法清除线程的局部变量并不是必须的操作,但它可以加快内存回收的速度。

(4) protected Object initialValue()返回该线程局部变量的初始值,该方法是一个protected的方法,显然是为了让子类覆盖而设计的。这个方法是一个延迟调用方法,在线程第1次调用get()或set(Object)时才执行,并且仅执行1次,ThreadLocal中的缺省实现直接返回一个null。 可以通过上述的几个方法实现ThreadLocal中变量的访问,数据设置,初始化以及删除局部变量,那ThreadLocal内部是如何为每一个线程维护变量副本的呢? 其实在ThreadLocal类中有一个静态内部类ThreadLocalMap(其类似于Map),用键值对的形式存储每一个线程的变量副本,ThreadLocalMap中元素的key为当前ThreadLocal对象,而value对应线程的变量副本,每个线程可能存在多个ThreadLocal。

三、内存泄漏问题

每个thread中都存在一个map, map的类型是ThreadLocal.ThreadLocalMap. Map中的key为一个threadlocal实例. 这个Map的确使用了弱引用,不过弱引用只是针对key. 每个key都弱引用指向threadlocal. 当把threadlocal实例置为null以后,没有任何强引用指向threadlocal实例,所以threadlocal将会被gc回收. 但是,我们的value却不能回收,因为存在一条从current thread连接过来的强引用. 只有当前thread结束以后, current thread就不会存在栈中,强引用断开, Current Thread, Map, value将全部被GC回收。所以得出一个结论就是只要这个线程对象被gc回收,就不会出现内存泄露,但在threadLocal设为null和线程结束这段时间不会被回收的,就发生了我们认为的内存泄露。其实这是一个对概念理解的不一致,也没什么好争论的。最要命的是线程对象不被回收的情况,这就发生了真正意义上的内存泄露。比如使用线程池的时候,线程结束是不会销毁的,会再次使用的就可能出现内存泄露 。(在web应用中,每次http请求都是一个线程,tomcat容器配置使用线程池时会出现内存泄漏问题)

beb9d058048227aaa302a9020134521f.png

ThreadLocal只是操作Thread中的ThreadLocalMap,每个Thread都有一个map,ThreadLocalMap是线程内部属性,ThreadLocalMap生命周期是和Thread一样的,不依赖于ThreadMap。

ThreadLocal通过Entry保存在map中,key为Thread的弱引用(GC时会自动回收),value为存入的变量副本,一个线程不管有多少个ThreadLocal,都是通过一个ThreadLocalMap来存放局部变量的,可以再源码中看到,set值时先获取map对象,如果不存在则创建,threadLocalMap初始大小为16,当容量超过2/3时会自动扩容。

四、总结

1.使用ThreadLocal,建议用static修饰 static ThreadLocal headerLocal = new ThreadLocal();

2.使用完ThreadLocal后,执行remove操作,避免出现内存溢出情况。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
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里的值 。 :一篇文章我们来分析一个JavaThreadLocal内存泄露的案例。分析问题的过程比结果更重要,理论结合实际才能彻底分析出内存泄漏的原因。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值