根本原因:由于Thread中包含变量ThreadLocalMap,因此ThreadLocalMap与Thread的生命周期是一样长,如果都没有手动删除对应key,都会导致内存泄漏。
测试方式:使用线程池启动新的线程,当新线程未结束时,GC无法回收ThreadLocalMap中的内存
测试代码:
public class ThreadLocalDemo {
public static void main(String[] args) {
ThreadLocalDemo demo = new ThreadLocalDemo();
demo.run();
}
public void run(){
ExecutorService executorService = Executors.newFixedThreadPool(1);
executorService.execute(new Task());
System.gc();
};
class Task implements Runnable{
@Override
public void run() {
ThreadLocal<byte[]> localString = new ThreadLocal<>();
localString.set(new byte[1024*1024*300]);
}
}
}
打开jdk下bin的jconsole.exe,链接ThreadLocalDemo
手动执行gc,内存仍有300m无法回收
修改程序,重新链接,线程停止后再次执行GC发送已经回收
public void run() throws InterruptedException {
ExecutorService executorService = Executors.newFixedThreadPool(1);
executorService.execute(new Task());
System.gc();
System.out.println("运行30s后停止线程");
Thread.currentThread().sleep(30000);
executorService.shutdown();
System.out.println("线程停止,运行GC查看");
Thread.currentThread().sleep(30000);
};