ThreadLocal内存泄漏

简述

ThreadLocal是什么,是java中用于实现线程内变量的工具类。允许每个线程都拥有自己的副本,从而实现线程隔离,解决多线程的共享对象的线程安全问题。

image-20240330210435879

ThreadLocal的使用

1
2
3
public static ThreadLocal<String> localVariable = new ThreadLocal<>(); // 新建
localVariable.set("他是沙雕"); // 设置值
String value = localVariable.get(); // 获取值

详述

实现原理

ThreadLocal的底层实现是一个特殊的map,可以理解往ThreadLocal设置一个xxx值,就是往这个特殊的map中设置一个key为当前线程,value为xxx的值。由于他的key只能为当前线程,所以在某个线程中set和get都不会影响其他线程,从而到达线程安全。下图的ThreadLocalMap就是拿个特殊的map。

image-20240330211735870

总体来说,java在运行的过程中会维护一个ThreadLocalMap,这个ThreadLocalMap的key为各个线程,value为各个线程想存放的东西,可以是String,可以是map,List等等。当时由于key为各个线程,所有每个线程只能由一个Entry ,也就是一个键值对(key-value),当然线程也可以不设置不存放。

image-20240330213034272

内存泄露

都知道ThreadLocal会内存泄漏,那么这个内存泄漏又是怎么回事。

都知道在java虚拟机中栈是私有的,而堆是共享的,所以ThreadLocalMap是存放在堆中的,而栈中存的只是一个引用,如下图。

image-20240330214507001

由于Entry extends WeakReference<ThreadLocal<?>>,所以可以看到,Entry中的key指向ThreadLocal是弱引用,也就是线程执行完之后,这个ThreadLocal就会被释放清理掉,但是由于ThreadLocalMap指向Entry是强引用,所以Entry不会被释放清理,如果这Entry一直不被清理释放,那么ThreadLocalMap的内存占用就会越来越大,以至于内存也漏。

所以为了防止这种问题的发生,在用完时,记得释放清理。

1
2
3
4
5
6
try {
threadLocal.set(value);
// 执行业务操作
} finally {
threadLocal.remove(); // 确保能够执行清理
}