详解ThreadLocal为什么会内存溢出原理
作者:Lxlxxx 发布时间:2023-11-09 18:45:26
前言
关于ThreadLocal (线程本地存储),从字面意思上看主要是存储一些本地变量,使它们能在一个线程内共用,与其他的线程进行数据隔离,保证了数据在一个线程内的安全性,日常的开发中,ThreadLocal的使用场景还是比较常见的,包括登陆信息的token的存储、连接管理一个线程持有一个链接,该连接可以在不同的方法之间进行传递,一个线程内数据共享,通过key,value的形式存储数据。
ThreadLocal源码分析
ThreadLocal类有一个静态内部类,ThreadLocalMap可以看到内部有个Entry 数组k就是ThreadLocal的引用,Entry 继承了WeakReference 说明Entry 的k是个弱引用,从这看来如果是弱引用那么就不会存在内存溢出,GC运行的时候,这个对象就会被回收掉,value则是存储的对象,而ThreadLocalMap则是由threadLocals来创建的,可以看到这两个变量的默认都是NULL。
p>只有当线程第一次调用的时候才会创建它。
ThreadLocal value内存溢出
前面讲到ThreadLocal的key是threadlocals是弱引用不会存在内存溢出,那么容易存在内存溢出的一定是它的value,它与current thread 存在一个强引用的关系,导致value无法进行回收,如果线程的对象一直不去销毁这个强引用的对象,那么导致这个关系一直存在就会出现内存溢出,
/**
* 内存溢出例子
*/
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);
}
});
}
堆内存设置50m
通过上面代码,创建线程池对创建的任务,放入threadlocal里面,可以看到出现了堆内存的溢出,存放的任务一直在引用没有得到释放导致堆内存空间不足。
p>我们在set值到threadLocal后面加入finally,调用它的remove方法来清除它的内存那么就不会发生内存溢出。
来看看remove的代码,可以看到获取当前线程的threadLocals,然后调用remove方法获取到全部的Entry数组,判断不为空,key也是当前的key则调用clear方法将数组清除,这样数组空间得到了释放自然就不会出现内存溢出。
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;
}
}
}
来源:https://juejin.cn/post/7181080568493244453


猜你喜欢
- 现在很多app的首页都有一个倒计时控件,比如说3秒或者5秒自动跳转界面,或者点击控件直接跳过首先,自定义控件CircleProgressba
- 安全无处不在,趁着放假读了一下 Shiro 文档,并记录一下 Shiro 整合 Spring Boot 在数据库中根据角色控制访问权限简介A
- 我在Eclipse/MyEclipse环境下都测试过了,都好使。需要2个组件,分别是: ext-4.0.2a.jsb2 spke
- 本文介绍了使用C#创建Windows服务的实例代码,分享给大家一、开发环境操作系统:Windows 10 X64开发环境:VS2015编程语
- 很多项目需要用到弹幕效果,尤其是在播放视频的时候需要一起显示别人发的弹幕,也包括自己的发的。今天就试着写了一下这个效果。思路就是将从右往左的
- 一、Android 个人手机通讯录开发数据存储:SQLite 数据库开发工具:Android Studio二、Phone Module 简介
- 类是使用关键字 class 声明的,如下面的示例所示:访问修饰符 class 类名 { //类成员: // Methods, prope
- 本文实例分析了Android编程画图之抗锯齿解决方法。分享给大家供大家参考,具体如下:在画图的时候,图片如果旋转或缩放之后,总是会出现那些华
- 针对字符串是数字和字母结合而进行的,如"a20"和"a9";比较而得出结果是"a20&qu
- 总结并复现了一下Unsafe在安全领域的一些应用0 前言unsafe里面有很多好用的方法,比如allocateInstance可以直接创建实
- 本文实例讲述了Android使用Sensor感应器实现线程中刷新UI创建android测力计的功能。分享给大家供大家参考,具体如下:前面一篇
- 前言数独是一种有趣的智力游戏,但是部分高难度数独在求解过程中经常出现大量单元格有多个候选数字可以填入,不得不尝试填写某个数字然后继续推导的方
- 本文实例为大家分享了C语言实现中国象棋的具体代码,供大家参考,具体内容如下运行截图实现思路老套路,二维数组存储棋盘,宏定义各种棋子,每次棋子
- 1. 确保你项目能编译通过,安装java jdk 环境填写环境变量2. 添加SpringBootServletInitializer的子类重
- 博主说:有时候,我们需要对数据库中现有的数据进行大量处理操作(例如表中的某个字段需要全部更新等),如果直接使用select * from t
- 前言Genymotion 来自于 AndroVM 这个开源项目,基于 x86 和 VirtualBox,支持 OpenGL 加速,可以用于
- 刚毕业的第一份工作是 java 开发,项目中需要用到 mybatis,特此记录学习过程,这只是一个简单 demo,mybatis 用法很多不
- 本文实例为大家分享了Android弹出菜单效果的具体代码,供大家参考,具体内容如下功能描述:用户单击按钮弹出菜单。当用户选择一个菜单项,会触
- 在网络信息高速发展的今天,移动设备的方便快捷已经深入人心,越来越多的开发人员会选择在移动设备上查看或编辑源代码。于是,Android平台上大
- 本文实例为大家分享了Android studio实现简单计算器的具体代码,供大家参考,具体内容如下需求分析及概要设计目的开发一个简单的计算器