【Java并发编程】导致JVM内存泄露的ThreadLocal详解

ThreadLocal及内存泄漏
(1)说明
ThreadLocal为每个线程都提供了变量的副本(ThreadLocalMap),使得每个线程在某一时间访问到的并非同一个对象,这样就隔离了多个线程对数据的数据共享;
ThreadLocal和Synchonized区别:
ThreadLocal和Synchonized都用于解决多线程并发访问。可是ThreadLocal与synchronized有本质的差别。synchronized是利用锁的机制,使变量或代码块在某一时该仅仅能被一个线程访问,ThreadLocal则是副本机制,此时不论多少线程并发访问都是线程安全的;
性能比较:ThreadLocal > AtomicInteger > ReentrantLock,因为ThreadLocal没有锁竞争;
(2)数据结构

一个当前线程对应一个ThreadLocalMap,ThreadLocalMap是ThreadLocal的静态内部类,用Entry数组来进行存储具体数据,Entry的key为ThreadLocal,value为存的值,ThreadLocalMap实例化是在Thread内部;
Entry的key(ThreadLocal)为WeakReference弱引用,通过Entry数组来存放线程可能需要的多个副本变量;
(3)作用及用处

  1. 线程间数据隔离,比如controller层使用ThreadLocal存储请求参数;
  2. Spring的事务管理器就是利用ThreadLocal将连接绑定到线程的,从而保证事务生效;
  3. 链路跟踪中的traceId传递也是利用了ThreadLocal;
  4. session会话管理;
    (4)ThreadLocal使用
    ThreadLocal类接口很简单,只有4个方法:

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

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

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

protected Object initialValue()
返回该线程局部变量的初始值,该方法是一个protected的方法,显然是为了让子类覆盖而设计的。这个方法是一个延迟调用方法,在线程第1次调用get()或set(Object)时才执行,并且仅执行1次。ThreadLocal中的缺省实现直接返回一个null。
(5)ThreadLocal内存泄漏
在这里插入图片描述

场景
线程池中的线程可以被重复利用,当ThreadLocal为null,要被垃圾收集器回收掉。但此时ThreadLocalMap的生命周期和Thread一样,它不会被回收。此时,ThreadLocalMap的key没了,但是value还在,造成了内存泄漏;

解决
使用线程池时,使用完ThreadLocal后,执行remove操作,避免出现内存泄漏;

为什么使用弱引用而不是强引用?
下面我们分两种情况讨论:
key 使用强引用:对ThreadLocal对象实例的引用被置为null了,但是ThreadLocalMap还持有这个ThreadLocal对象实例的强引用,如果没有手动删除,ThreadLocal的对象实例不会被回收,导致Entry内存泄漏。
key 使用弱引用:对ThreadLocal对象实例的引用被被置为null了,由于ThreadLocalMap持有ThreadLocal的弱引用,即使没有手动删除,ThreadLocal的对象实例也会在下次gc时被回收。而value在下一次ThreadLocalMap调用set,get,remove都有机会被回收。
比较两种情况,我们可以发现:由于ThreadLocalMap的生命周期跟Thread一样长,如果都没有手动删除对应key,都会导致内存泄漏,但是使用弱引用可以多一层保障。

(6)总结

JVM利用设置ThreadLocalMap的Key为弱引用,来避免ThreadLocal对象内存泄露。
JVM利用调用remove、get、set方法的时候,回收弱引用。
当ThreadLocal存储很多Key为null的Entry的时候,而不再去调用remove、get、set方法,那么将导致内存泄漏。
使用线程池+ThreadLocal时要小心,因为这种情况下,线程是一直在不断的重复运行的,从而也就造成了value可能造成累积的情况。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值