首先我们要先了解什么是泄漏问题和什么是内存溢出
内存泄漏表示程序员申请了内存,但是该内存一直无法被释放
内存溢出表示申请内存不足,就会报错
为何引发内存泄漏问题
- 因为每个线程都有自己独立的ThreadLocalMap对象,key为ThreadLocal,value为变量值。Key为ThreadLocal作为Entry对象的key,是弱引用,当ThreadLocal指向null的时候,Entry对象中的key变为null,该对象一直无法被垃圾回收机制回收,一直占用到了系统内存,有可能发生内存泄漏的问题
接下来我来证实这一观点
public class ThreadLocalTest {
public static void main(String[] args) {
ThreadLocal<String> threadLocal = new ThreadLocal<>();
threadLocal.set("donglin");
threadLocal = null;
Thread thread = Thread.currentThread();
System.out.println(thread);
}
}
我们进行debug,发现threadlocal里面还有值,并且还有引用
线程中Threadlocal设置为null(也可以理解为该线程方法执行完毕了),会断开主线程和堆内存之间的引用链,当GC回收的时候,不会回收堆内存,因为还有一条指向堆内存的引用链,从而会导致内存泄漏
那如何解决这个ThreadLocalMap引用链呢
使用revome方法
public class ThreadLocalTest {
public static void main(String[] args) {
ThreadLocal<String> threadLocal = new ThreadLocal<>();
threadLocal.set("donglin");
//ThreadLocalMap与内存中的ThreadLocal断开引用
threadLocal.remove();
//threadLocal与堆内存中的ThreadLocal断开引用
threadLocal = null;
//GCRoot引用链就会发现ThreadLocal,没有人引用,就会清理掉该对象,避免内存泄漏问题
Thread thread = Thread.currentThread();
System.out.println(thread);
}
}
为啥要使用弱引用而不使用强引用
当function01方法执行完毕后,栈帧销毁强引用 tl 也就没有了。但此时线程的ThreadLocalMap里某个entry的key引用还指向这个对象
若这个key引用是强引用,就会导致key指向的ThreadLocal对象及v指向的对象不能被gc回收,造成内存泄漏;
若这个key引用是弱引用就大概率会减少内存泄漏的问题(还有一个key为null的雷)。使用弱引用,就可以使ThreadLocal对象在方法执行完毕后顺利被回收且Entry的key引用指向为null。
此后我们调用get,set或remove方法时,就会尝试删除key为null的entry,可以释放value对象所占用的内存。