详解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。

详解ThreadLocal为什么会内存溢出原理

详解ThreadLocal为什么会内存溢出原理

p>只有当线程第一次调用的时候才会创建它。

详解ThreadLocal为什么会内存溢出原理

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为什么会内存溢出原理

通过上面代码,创建线程池对创建的任务,放入threadlocal里面,可以看到出现了堆内存的溢出,存放的任务一直在引用没有得到释放导致堆内存空间不足。

详解ThreadLocal为什么会内存溢出原理

p>我们在set值到threadLocal后面加入finally,调用它的remove方法来清除它的内存那么就不会发生内存溢出。

详解ThreadLocal为什么会内存溢出原理

来看看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

标签:ThreadLocal,内存溢出,内存
0
投稿

猜你喜欢

  • SpringBoot通过源码探究静态资源的映射规则实现

    2022-03-26 19:05:53
  • Java System类两个常用方法代码实例

    2023-02-01 13:52:15
  • Idea中SpringBoot多模块项目的建立实现

    2023-11-08 07:52:34
  • Java实战之实现用户登录

    2022-08-03 14:42:55
  • 进度条ProgressBar及ProgressDialog(实例)

    2021-09-24 01:07:42
  • Android手机闹钟服务AlarmManagerk开发案例

    2023-05-21 14:15:28
  • Unity实现注册登录模块

    2023-11-24 04:55:22
  • WPF使用WrapPanel实现虚拟化效果

    2021-07-04 14:53:53
  • 根据灰度值填充字符-单文件单线程版

    2023-10-12 00:50:24
  • Java ThreadPoolExecutor线程池有关介绍

    2022-11-21 02:03:20
  • 浅谈Java后台对JSON格式的处理操作

    2023-02-16 07:28:36
  • Java解析DICOM图之如何获得16进制数据详解

    2023-06-15 17:37:29
  • java高并发之线程组详解

    2023-02-15 21:30:25
  • Android标题栏中添加返回按钮功能

    2022-09-16 05:52:47
  • 解决Java & Idea启动tomcat的中文乱码问题

    2022-05-31 05:48:35
  • Java关键字volatile详析

    2023-01-21 21:51:11
  • mybatisPlus返回Map类型的集合

    2022-01-31 13:37:07
  • Android SeekBar充当Progress实现兔兔进度条Plus

    2021-12-05 16:36:25
  • 一篇文章带你入门Java数据类型

    2022-06-10 09:25:44
  • LINQ基础之Join和UNION子句

    2022-04-09 23:27:50
  • asp之家 软件编程 m.aspxhome.com