详解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
猜你喜欢
- SpringCloud 整合ribbon的时候出现了这个问题java.lang.IllegalStateException: No inst
- 刚开始我以为熔断和降级是一体的,以为他们必须配合使用; 只不过名字不一样而已,但是当我经过思考过后,发现他们其实不是一个东西;降级什么是服务
- 代码如下import java.util.concurrent.Callable;import java.util.concurrent.E
- 先上图下拉刷新跟原生开发一样,下拉刷新在flutter里提供的有组件实现 RefreshIndicator一直不明白为啥组件中都提供下拉刷新
- 实现原理: 长连接的维持,是要客户端程序,定时向服务端程序,发送一个
- 本文实例为大家分享了opencv实现轮廓高斯滤波平滑的具体代码,供大家参考,具体内容如下一个小测试的题目:在图像上点选,找到与点选处相邻的颜
- 面试题1:你了解线程池么?简单介绍一下。java提供的一个java.util.concurrent.Executor接口的实现用于创建线程池
- 一、基本概念(重要)Integer 是 int 的包装类,int 则是 java 的一种基本数据类型;Integer 变量必须实例化后才能使
- 本文实例为大家分享了flutter实现appbar下选项卡切换的具体代码,供大家参考,具体内容如下TabBar 、Tab、TabBarVie
- 前言先放一个官网吧,其实本案例就是根据官网案例来的,只是进行了修改配置。Mybatis-plus官网一、搭建一个springboot项目&n
- Android的消息机制几乎是面试必问的话题,当然也并不是因为面试,而去学习,更重要的是它在Android的开发中是必不可少的,占着举足轻重
- 1.登录腾讯云点击登录选择浏览器登录。输入用户名 按回车键 然后输入 密码。2.安装java环境直接命令:yum -y install ja
- java项目中常用maven工具来进行工程管理,但经常遇到的一个问题是生成的jar包越来越大,编译一次工程越来越慢。怎么有效地去除冗余依赖,
- 这篇文章主要介绍了JPA save()方法将字段更新为null的解决方案,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考
- 一、输入输出流对象cout:标准输出流cerr:标准出凑 和cout(只是用于如果是错误时要输出的)cin :&nb
- 本文实例为大家分享了OpenCV实现直线检测并消除的具体代码,供大家参考,具体内容如下很简单,代码如下#include<iostrea
- 线程安全当多个线程访问某个类时,不管运行时环境采用何种调度方式或者这些进程将如何交替执行,并且在主调代码中不需要任何额外的同步或协调,这个类
- 1、首先导入solrj需要的的架包2、需要注意的是低版本是solr是使用SolrServer进行URL实例的,5.0之后已经使用SolrCl
- 1、Jetbrains官网下载IntelliJ IDEA1.1 官方网站http://www.jetbrains.com/idea/&nbs
- 在Spring Boot Actuator中提供很多像health、metrics等实时监控接口,可以方便我们随时跟踪服务的性能指标。Spr