首页 > 系统相关 >ThreadLocal 在实战场景下的内存泄漏、逻辑混乱问题总结

ThreadLocal 在实战场景下的内存泄漏、逻辑混乱问题总结

时间:2024-01-19 15:32:26浏览次数:32  
标签:泄漏 OOM 复用 ThreadLocal 线程 内存


前言

很早之前虽然看过 ThreadLocal 的源码,但是对于真实业务场景下可能存在的问题没有做过总结,刚好前几天在分析 Mybatis 内存泄漏的问题,想着 ThreadLocal 不是也可能会发生内存泄漏吗?于是乎本文出现了。

本文相关博客

1 : ThreadLocal还存在内存泄漏?源码级别解读

2 : 高质量实现单文件导入、导出功能(使用EasyExcel )

大白话介绍:什么是内存泄漏?

内存泄漏:我个人理解就是由于程序设计失误,用完的内存空间没有得到应得的释放,这就是内存泄漏。

1 . 而我们要知道程序的运行需要被分配内存,简单来说我们每创建一个 List 、Map …并往里面塞数据的时候,都需要开辟内存空间来存储,当你只顾着塞数据,而不清空数据的时候,内存只会只增不减,当达到了物理机最大内存时就会发生 OOM。所以我们在编写代码的时候需要注意是否会出现内存泄漏。

2 . 从 JVM 角度 OOM 的出现是因为,Eden 区发生 Young Gc的时候,会进行回收 Eden 、 Surviver0 、 Surviver1 区里面没有被使用的对象的堆内存,而 Eden 区那些正在使用的对象会从 Eden 区,放到空的 Surviver 的 To 区里面,此时 Surviver 区存在俩种情况,一种是那些正在使用的对象年龄计数器达到了 15 会从 Surviver 区转到 老年代,还有一种情况就是 Surviver 区满了,放不下 Eden 区过来的对象了,这个对象直接放到老年代。当老年代满了的时候触发 Old Gc ,GC 完了之后发现老年代仍然放不下新生代过来的数据,此时就会爆堆空间满(OOM)的错误

线程池复用线程 bug 举例一

理论上:有源源不断的不同的线程一直往 ThreadLocal 中 set 数据,而且你还没有进行 Remove 方法的调用是会造成 OOM,但是现在主流的 Spring Boot 开发内置了一个 Tomcat 里面的线程是复用的,也就是说 10 个请求里面就有可能 2 个请求是同个线程发起的。我觉得即使是有可能造成内存泄漏 OOM,这个问题暴露的几率也很低,因为同线程多次 Set 值会被覆盖,但是这样会引申出另外一个问题,就是由于内置 TomCat 线程的复用,让原本线程隔离的 ThreadLocal 变得不线程隔离了。造成一些脏数据泄漏。只要是涉及到线程池线程复用的地方都会存在这个问题。那怎么去解决呢?答:当前线程代码逻辑执行完调用 Remove 方法就行。可以看下图,执行了 4 个异步任务,异步任务 2 居然也拿到了数据。原因是 CompletableFuture 内部也是维护了一个线程池,任务 1 和任务 2 都是复用的一个线程,所以都打印了 zzh1。

ThreadLocal 在实战场景下的内存泄漏、逻辑混乱问题总结_threadlocal

Spring Boot 内置 tomcat 线程复用 bug 举例二

编写如下俩个接口,按道理来说只有 getInfo 才会返回 test,getInfo2 返回 null 才对。

ThreadLocal 在实战场景下的内存泄漏、逻辑混乱问题总结_内存泄漏_02


测试结果如下:俩个接口都返回了 test 值诶,原因就是内置 TomCat 复用了线程。

ThreadLocal 在实战场景下的内存泄漏、逻辑混乱问题总结_复用_03


ThreadLocal 在实战场景下的内存泄漏、逻辑混乱问题总结_java_04

附页彩蛋

可能大家在本地测试:Spring Boot 内置 tomcat 线程复用 bug 举例二 中的现象的时候,getInfo 2有的时候是返回 null,而有的时候返回的却是 test 这个诡异现象,那是因为我忘记告诉大家要设置一个 Tomcat 的一个参数,最大线程数设置成 1 这样测试效果就明显了。

server:
  port: 1133
  tomcat:
    threads:
      max: 2

总结

避免内存泄漏的最好解决办法就是使用完 ThreadLoacl 中的数据的时候,同时调用一下 Remove 方法。其实之前我在写多线程导入 Excel 的源码里面也使用到了 ThreadLocal 存放每个线程解析到的 Excel 数据,如果那里不进行 Remove 方法的调用,一次几十万的数据解析出来内存一直占着而不进行释放,多个几次导入就很容易造成 OOM 了。大家注意一下就好了。

ThreadLocal 在实战场景下的内存泄漏、逻辑混乱问题总结_内存泄漏_05

小咸鱼的技术窝

关注不迷路,日后分享更多技术干货,B站、微信公众号同名,名称都是(小咸鱼的技术窝)更多详情在主页


标签:泄漏,OOM,复用,ThreadLocal,线程,内存
From: https://blog.51cto.com/u_16414043/9330414

相关文章

  • 内存颗粒, 正片, 白片, 黑片。
    问题:不理解客户说的 内存颗粒。 网上的截图:       总结:内存颗粒应该是在PC上的内存的叫法,因为一片内存是一长条,上面的内存芯片像颗粒一样,干脆,内存颗粒就代指内存条了。但是在嵌入式板卡上,没有内存条,我们直接用的是内存芯片也就是内存颗粒......
  • C++ 共享内存ShellCode跨进程传输
    在计算机安全领域,ShellCode是一段用于利用系统漏洞或执行特定任务的机器码。为了增加攻击的难度,研究人员经常探索新的传递ShellCode的方式。本文介绍了一种使用共享内存的方法,通过该方法,两个本地进程可以相互传递ShellCode,从而实现一种巧妙的本地传输手段。如果你问我为何在本地了......
  • 127.nginx内存池创建和重置函数
    127.nginx内存池创建和重置函数#defineNGX_MAX_ALLOC_FROM_POOL(ngx_pagesize-1)//能从内存池中分配的最大的内存1.小块大块内存分界#defineNGX_DEFAULT_POOL_SIZE(16*1024)//默认池子大小#defineNGX_POOL_ALIGNMENT16//内存分配的字节对齐数#define......
  • 使用valgrind分析Linux程序内存泄漏
     1      安装...12      参数说明...13      使用问题...2 1        安装直接使用命令行安装sudoapt-getinstallvalgrind源代码下载安装下载最新版本:http://valgrind.org/downloads/current.html#current安装命令:tar-jxvfvalg......
  • 深入理解Java中的ThreadLocal
    第1章:引言大家好,我是小黑。今天咱们来聊聊ThreadLocal。首先,让咱们先搞清楚,ThreadLocal是个什么玩意儿。简单说,ThreadLocal可以让咱们在每个线程中创建一个变量的“私有副本”。这就意味着,每个线程都可以独立地改变自己的副本,而不会影响其他线程。这就像是每个人都有自己的笔记......
  • 企业信息防泄漏管理的理念是什么?
    ​在这个数字化的时代,信息数据的安全已经成为企业发展的关键要素。随着网络技术的飞速发展,信息安全的重要性日益凸显,它关乎企业的生死存亡。在企业的信息系统中,信息泄露是最常见和最严重的风险之一。因此,建立一套全面有效的信息防泄漏管理体系,成为企业信息安全管理的重要任务。本......
  • idea 项目编译内存溢出解决配置
    https://blog.csdn.net/malin970824/article/details/89843478 以下几种方式都可尝试下:1.在idea安装的bin目录修改配置文件 -Xms512m-Xmx2024m-Xss4M-XX:MaxPermSize=2024m 2.修改settings 3.修改tomcat-server-Xms512m-Xmx2024m-Xss4M-XX:PermSize=512M-XX:......
  • 梦幻内存!全何为AMD撕裂者打造192GB DDR5-7200
    1月16日消息,全何科技(V-Color)宣布,面向AMD锐龙线程撕裂者7000系列处理器,推出顶级的192GBDDR5内存套装,频率最高可达7200MHz。AMD撕裂者7000系列支持四通道DDR5内存,单系统可以插四根,虽然不及撕裂者PRO7000系列的八通道,但在消费级领域也是无敌的存在。全何新内存采用精选的SK海......
  • 记一次 .NET某道闸收费系统 内存溢出分析
    一:背景1.讲故事前些天有位朋友找到我,说他的程序几天内存就要爆一次,不知道咋回事,找不出原因,让我帮忙看一下,这种问题分析dump是最简单粗暴了,拿到dump后接下来就是一顿分析。二:WinDbg分析1.程序为什么会暴程序既然会爆,可能是虚拟地址受限,也可能是系统内存不足,可以用!address......
  • C++内存分配揭秘:new操作符::operator new和Placement new的区别
     在C++中,new 操作符、::operatornew 和placementnew是用于动态内存分配的工具,但它们有不同的用法和行为。以下是它们的区别和用法的详细实例:1.new操作符new 操作符用于在堆上动态分配内存,并调用对象的构造函数初始化对象。#include<iostream>classMyClass{p......