一文搞懂ThreadLocal内存泄露问题

背景

最近工作中,在写一些sdk和封装一些springboot starter中,经常会用到threadLocal,看了一些源码发现了一个问题 那就是定义的ThreadLocal变量基本全是用static修饰的,心中的疑问是static修饰的变量不是类共享的吗?多线程下不应该是不用static来修饰的吗? 带着这两个疑问写下了这篇博客

为何通常将 ThreadLocal 变量设置为static?

首先我们需要了解两个概念

什么是threadLocal

jdk1.2版本中提供java.lang.ThreadLocal,threadLocal为解决多线程程序并发问题提供了一种新的思路。使用这个类可以很简介的编写出优美的多线程程序,ThreadLocal是Thread的局部变量,提供线程范围内的局部变量,这种变量在线程的生命周期内起作用。作用:提供一个线程内公共变量,减少同一个线程内多个函数或者组件之间一些公共变量的传递的复杂度,或者为线程提供一个私有的变量副本,这样每一个线程都可以随意修改自己的变量副本,而不会对其他线程产生影响。

Static

在java语言中,static表示“静态”的意思,使用场景修饰成员变量、成员方法、静态代码块等。static的主要作用在于创建独立于具体对象的域变量或方法。
静态变量和非静态变量的区别:
静态变量被所有实例共享,在内存中只有一个副本(存放在方法区),它当且仅当类初次加载时会被初始化
非静态变量:
是对象所拥有的,在创建对象的时候初始化,存在多个副本。各个对象互不影响。
也就是说,在一个线程内,没有被static修饰的ThreadLocal变量实例,会随着所在的类多次创建而被多次实例化,虽然threadLocal限制了变量的作用域,但这样频繁创建变量实例是没有必要的。

变量为什么用static修饰呢?

为了避免重复创建TSO (thred specific object,即与线程相关的变量)。我们知道,一个threadLocal实例对应当前线程一个TSO实例。
因此如果把threadLocal声明为某个类的实例变量(而不是静态常量),那么每创建一个该类的实例就会导致一个新的TSO实例被创建。显然,这些被创建的TSO实例是同一个类的实例。于是,同一个线程可能会访问到同一个TSO(指类)的不同实例,这即便不会导致错误,也会导致浪费(重复创建等同的对象)。

ThreadLocal底层实现

我们先简单回忆一下ThreadLocal的底层实现
一文搞懂ThreadLocal内存泄露问题_第1张图片
一文搞懂ThreadLocal内存泄露问题_第2张图片

我们可以知道JDK的线程本地化ThteadLocal 是存储在每个线程Thread 内部的ThreadLocalMap里的,且ThreadLocalMap包含了一个Entry数组。
每个Entry存储了一个ThreadLocal和对应Value值,也就是说Thread可以存储多个threadLocal对象。

ThreadLocal 会有内存溢出问题吗?

一文搞懂ThreadLocal内存泄露问题_第3张图片

当使用线程的时候。我们使用一个线程thread来执行任务,当thread线程执行任务退出之后,该线程里所持有的ThreadLocalMap的对象也就没有强引用了,所以就可以被JDK垃圾回收器回收了,那么threadlocalmap包含的threadlocal也就被回收掉了。
但是往往我们在代码编程的时候,往往是很少直接使用这种线程的,因为频繁创建线程对机器的消耗是很大的。而且线上正在跑的机器也不支持这样频繁创建线程的。

针对线程池情况

你可能感兴趣的:(java,spring,jvm)