用例:
先给大家看一段代码测试下:我们先设置了JVM堆内存的大小为30M。
new Thread(() -> {
System.out.println("1==" + Runtime.getRuntime().freeMemory());
threadLocal.set(new byte[1024 * 1024 * 10]);
System.out.println("2==" + Runtime.getRuntime().freeMemory());
// threadLocal.remove();
try {
Thread.sleep(5000);
System.out.println("3==" + Runtime.getRuntime().freeMemory());
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
new Thread(() -> {
try {
Thread.sleep(2000);
System.gc();
System.out.println("3==" + Runtime.getRuntime().freeMemory());
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("1==" + Runtime.getRuntime().freeMemory());
threadLocal.set(new byte[1024 * 1024 * 10]);
System.out.println("2==" + Runtime.getRuntime().freeMemory());
// threadLocal.remove();
try {
Thread.sleep(5000);
System.out.println("3==" + Runtime.getRuntime().freeMemory());
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
1==23912512
2==13258592
3==17776072
1==17776072
Exception in thread "Thread-1" java.lang.OutOfMemoryError: Java heap space
at com.thread.demo.Demo.lambda$main$1(Demo.java:68)
at com.thread.demo.Demo$$Lambda$2/114935352.run(Unknown Source)
at java.lang.Thread.run(Thread.java:745)
3==17734224
上面这段代码在使用ThreadLocal后没有及时调用threadLocal.remove()的结果,程序出现内存溢出错误。
当我们及时调用threadLocal.remove()后程序就是正常的了。
导出出现内存泄漏的原因:
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
我们都知道Thread类中有一个变量为 ThreadLocal.ThreadLocalMap threadLocals = null;
当我们在调用set方法时会先获取到当前线程,然后对当前线程的ThreadLocalMap对象进行set key value键值对。
如果当前线程一直存活,会导致对应的value一直存在,造成内存泄漏。
并且由于key是弱引用是会被垃圾回收器所回收的,这时候ThreadLocalMap中就会存在一个key为null value不为null的键值对。
如果执行了threadLocal.remove()方法后。
对应的value的引起就会被去掉,就是将null赋值给对应的这个变量。
这时候垃圾回收器就会回收掉这个value了。
注意事项:
特别是我们在使用线程池的时候,线程池中配置的核心线程是永远存在的,这时候一定要注意规范的调用threadLocal.remove()方法。