本文说说ThreadLocal由于使用不当造成的内存泄漏问题
Thead和ThreadLocal的内存状况如下图,不了解的同学参考: 图解ThreadLocal核心原理
如果线程太多,每个线程的value值都很大,是不是会造成内存溢出,写个程序模拟一下OOM的场景
1、程序模拟一下OOM
public class ThreadLocalTest {
public static void main(String[] args) throws InterruptedException {
testOOM();
}
private static void testOOM() throws InterruptedException {
int nThreads = 101;
ExecutorService executorService = Executors.newFixedThreadPool(nThreads);
for (int i = 0; i < nThreads; i++) {
executorService.execute(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
threadLocal.set(new BigObject());
// 业务处理
}
});
}
Thread.sleep(10 * 1000);
}
static class BigObject {
byte[] _1M_bytes = new byte[1024 * 1024];
}
}
//
使用 -Xms100m -Xmx100m
参数运行程序
java.lang.OutOfMemoryError: Java heap space
at com.yh.stu.rocketmq.ThreadLocalTest$BigObject.<init>(ThreadLocalTest.java:67)
at com.yh.stu.rocketmq.ThreadLocalTest$1.run(ThreadLocalTest.java:28)
2、ThreadLocal OOM原因分析
为什么会OOM?因为线程池有101个线程,每一个线程都会有个Map,每个Map中存放了1M大小的对象,当循环执行到100左右的时候,堆内存就放满了,再向线程中放入Map时就OOM了
解决办法 :在// 业务处理
的后面加入threadLocal.remove()
,就不会OOM了,垃圾回收器会回收线程中的new BigObject()
对象的空间
3、Thread中的 Map
从前文中我们感觉到,用户的数据是存在Thread的Map中的,ThreadLocal对象只是作为Map的key的方式存在着
key是虚引用,内存不够垃圾回收时会回收,但value却不会, 看下图
执行 threadLocal.remove()
方法,将引用置为null,value对应的对象BigObject
没有引用指向它,可以被回收
附录-WeakReference使用
运行参数 -Xms100m -Xmx100m
public static void testWeakReference() throws InterruptedException {
while(true){
new Thread(){
@Override
public void run() {
WeakReference<BigObject> wref
= new WeakReference<>(new BigObject());
try {
Thread.sleep(Long.MAX_VALUE);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}.start();
Thread.sleep(100);
}
}
这个程序不会造成内存溢出,因为弱引用在内存不足时会被垃圾回收器回收