关于threadlocal (线程本地存储),从字面意思上看主要是存储一些本地变量,使它们能在一个线程内共用,与其他的线程进行数据隔离,保证了数据在一个线程内的安全性,日常的开发中,threadlocal的使用场景还是比较常见的,包括登陆信息的token的存储、连接管理一个线程持有一个链接,该连接可以在不同的方法之间进行传递,一个线程内数据共享,通过key,value的形式存储数据。
threadlocal源码分析
/**
* 内存溢出例子
*/
public class threadlocaltest {
static class mytask{
//定义10m的byte数组
private byte[] bytes =new byte[10 *1024 * 1024];
}
private static threadlocal<mytask> threadlocal = new threadlocal();
public static void main(string[] args) throws interruptedexception {
// 5个核心线程、5个最大线程、队列长度100
threadpoolexecutor threadpoolexecutor =new threadpoolexecutor(5, 5, 60,
timeunit.seconds, new linkedblockingqueue<>(100));
for (int i = 0; i < 10; i++) {
//执行任务
executetask(threadpoolexecutor);
thread.sleep(1000);
}
}
private static void executetask(threadpoolexecutor threadpoolexecutor){
threadpoolexecutor.execute(new runnable() {
@override
public void run() {
system.out.println("创建mytask对象");
mytask mytask =new mytask();
threadlocal.set(mytask);
}
});
}
threadlocal类有一个静态内部类,threadlocalmap可以看到内部有个entry 数组k就是threadlocal的引用,entry 继承了weakreference 说明entry 的k是个弱引用,从这看来如果是弱引用那么就不会存在内存溢出,gc运行的时候,这个对象就会被回收掉,value则是存储的对象,而threadlocalmap则是由threadlocals来创建的,可以看到这两个变量的默认都是null。
p>只有当线程第一次调用的时候才会创建它。
threadlocal value内存溢出
前面讲到threadlocal的key是threadlocals是弱引用不会存在内存溢出,那么容易存在内存溢出的一定是它的value,它与current thread 存在一个强引用的关系,导致value无法进行回收,如果线程的对象一直不去销毁这个强引用的对象,那么导致这个关系一直存在就会出现内存溢出,堆内存设置50m
通过上面代码,创建线程池对创建的任务,放入threadlocal里面,可以看到出现了堆内存的溢出,存放的任务一直在引用没有得到释放导致堆内存空间不足。
p>我们在set值到threadlocal后面加入finally,调用它的remove方法来清除它的内存那么就不会发生内存溢出。
public void remove() {
threadlocalmap m = getmap(thread./currentthread/());
if (m != null)
m.remove(this);
}
private void remove(threadlocal<?> key) {
entry[] tab = table;
int len = tab.length;
int i = key.threadlocalhashcode & (len-1);
for (entry e = tab[i];
e != null;
e = tab[i = nextindex(i, len)]) {
if (e.get() == key) {
e.clear();
expungestaleentry(i);
return;
}
}
}
来看看remove的代码,可以看到获取当前线程的threadlocals,然后调用remove方法获取到全部的entry数组,判断不为空,key也是当前的key则调用clear方法将数组清除,这样数组空间得到了释放自然就不会出现内存溢出。
总结
threadlocal在使用的时候一定到进行remove,这也是一个比较常见的内存溢出的例子,希望大家引以为戒。
联系客服