ject v) {
super(k);
value = v;
}
}
弱引用的常见用法是:
WeakReference<RoleDTO> weakReference = new WeakReference<>(new RoleDTO());
因此,在Entry
中,k
代表ThreadLocal
对象,它是弱引用。v代表ThreadLocal
管理的那个value
,是强引用。
内存泄漏
内存泄漏是指无用对象(不再使用的对象)持续占有内存或无用对象的内存得不到及时释放,从而造成内存空间的浪费称为内存泄漏。随着垃圾回收器活动的增加以及内存占用的不断增加,程序性能会逐渐表现出来下降,极端情况下,会引发OutOfMemoryError
导致程序崩溃。
内存泄漏问题主要在线程池中出现,因为线程池中的线程是不断执行的,从任务队列中不断获取新的任务执行。但是任务中可能有ThreadLocal
对象,这些对象的ThreadLocal
会保存在线程的ThreadLocalMap
中,因此ThreadLocalMap
会越来越大。
但是ThreadLocal
是由任务(worker)传入的,一个任务执行结束后,对应的ThreadLocal
对象会被销毁。线程中的关系是: Thread -> ThreadLoalMap -> Entry<ThreadLocal, Object>
。ThreadLocal
由于是弱引用会,在GC的时候会被销毁,这会导致 ThreadLoalMap
中存在Entry<null, Object>
。
使用remove()
由于线程池中的线程一直在运行,如果不对ThreadLoalMap
进行清理,那Entry<null, Object>
会一直占用内存。remove()
方法会清除key==null
的Entry
。
使用static修饰
将ThreadLocal
设置成static
可以避免一个线程类多次传入线程池后重复创建Entry
。例如,有一个用户定义的线程
public class Test implements Runnable{
private static ThreadLocal<Integer> local = new ThreadLocal<>();
@Override
public void run() {
// do something
}
}
使用线程池处理10个任务。那么线程池中每个用来处理任务的线程的Thread.ThreadLocalMap
中都会保存一个Entry<local, Integer>
,由于添加了static
关键字,所有每个线程中的Entry
中的local
变量引用的都是同一个变量。这时就算发生内存泄漏,所有的Test类也只有一个local
对象,不会导致内存占用过多。
@Test
void contextLoads() {
Runnable runnable = () -> {
System.out.println(Thread.currentThread().getName());
};
for(int index = 1; index <= 10; ++index){
taskExecutor2.submit(new com.example.test1.service.Test());
}
}