探究ThreadLocal和ThreadPoolExecutor中的内存泄露风险与防范策略

news2024/12/28 20:14:11

探究ThreadLocal和ThreadPoolExecutor中的内存泄露风险与防范策略

本文将探讨ThreadLocal和ThreadPoolExecutor中可能存在的内存泄露问题,并提出相应的防范策略。

ThreadPoolExecutor的内存泄露问题

ThreadPoolExecutor是一个线程池类,它可以管理和复用线程,从而提高程序的性能和稳定性。但是,如果使用不当,ThreadPoolExecutor也会导致内存泄露问题。

首先来说,如果我们在使用ThreadPoolExecutor时没有正确地关闭线程池,就会导致线程一直存在,从而占用大量的内存。为了避免这种情况的发生,我们可以在程序结束时手动关闭线程池。具体来说,我们可以在finally块中调用shutdown方法,从而确保线程池一定会被关闭。

ThreadPoolExecutor executor = new ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime, TimeUnit.SECONDS, workQueue);
try {
    // do something
} finally {
    executor.shutdown();
}

不过在实际生产过程中,大多数时候并不能关闭线程池,为了在无法关闭线程池的运行生产环境中防止内存泄漏

  1. 限制线程池的大小:确保线程池的大小适合工作负载和可用系统资源。过大的线程池可能导致过多的内存消耗,同时尽可能复用线程池,避免在项目中过多的创建线程池
  2. 使用有界队列:考虑使用有界队列(例如ArrayBlockingQueue)而不是无界队列。有界队列可以控制任务的排队数量,避免无限制的内存增长。
  3. 优化任务的执行时间:尽量减少任务的执行时间,避免长时间的任务阻塞线程池的线程。
  4. 定期监控线程池的状态:通过监控线程池的活动线程数、队列大小和任务执行情况,及时发现异常情况并进行调整。

ThreadLocal的内存泄露问题

ThreadLocal是一个多线程编程中常用的工具类,它允许我们在每个线程中存储和获取对象,而不必担心线程安全问题。但是,如果使用不当,ThreadLocal也会导致内存泄露问题。
通常情况下,我们会在使用完ThreadLocal后将其置为null,以便垃圾回收器可以回收它所占用的内存。但是,如果我们在某些情况下没有将其置为null,那么它就会一直占用内存,直到程序结束。
为了避免这种情况的发生,我们可以使用ThreadLocal的remove方法手动删除已经不再需要的变量。具体来说,我们可以在finally块中调用remove方法,从而确保变量一定会被删除。

ThreadLocal<Object> threadLocal = new ThreadLocal<Object>();
try {
    threadLocal.set(new Object());
    // do something
} finally {
    threadLocal.remove();
}

实际项目中在线程池中使用ThreadLocal导致内存溢出的案例,背景是在线程池中发送数据到kafka,并自定义了拒绝策略,在拒绝策略中把拒绝的相关信息打印出来。模拟相关业务代码

public class ThreadPoolUtil {
   private static ThreadLocal<ThreadLocalMemoryEntity>threadLocal= new ThreadLocal<>();
   //处理业务的线程池  核心参数设置小保证能进入拒绝策略
   private static final ThreadPoolExecutorcompensateBatchPool= new ThreadPoolExecutor(
         10, 10, 1, TimeUnit.SECONDS, new ArrayBlockingQueue(1)
         , new LogPolicy());
   //模拟客户端发送请求的线程池
   private static final ThreadPoolExecutorsimulateReqPool= new ThreadPoolExecutor(
         1000, 1000, 1, TimeUnit.SECONDS, new ArrayBlockingQueue(1000));

   public static ThreadPoolExecutor getCompensateBatchPool() {
      returncompensateBatchPool;
   }

   public static ThreadPoolExecutor getSimulateReqPool() {
      returnsimulateReqPool;
   }

   public static ThreadLocal<ThreadLocalMemoryEntity> getThreadLocal() {
      returnthreadLocal;
   }
   // 拒绝策略
   public static class LogPolicy implements RejectedExecutionHandler {
      @Override
      public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
         ThreadLocalMemoryEntity threadLocalMemoryEntity =threadLocal.get();
         // 模拟记录内容
         System.out.println("执行拒绝策略:"+ threadLocalMemoryEntity.getName());
      }
   }
}

业务实体

@Data
public class ThreadLocalMemoryEntity {
	private String name;

	private byte[] data = new byte[1024*1024];
}

idea配置堆内存空间 Xms512m -Xmx512m

业务代码

public class ThreadLocalMemoryLeakExample {
	private static ThreadLocal<ThreadLocalMemoryEntity> threadLocal = ThreadPoolUtil.getThreadLocal();
	private static ThreadPoolExecutor compensateBatchPool = ThreadPoolUtil.getCompensateBatchPool();
	private static ThreadPoolExecutor SimulateReqPool = ThreadPoolUtil.getSimulateReqPool();

	public static void main(String[] args) {
		try {
			for (int i = 0; i < 10000; i++) {
				ThreadLocalMemoryEntity threadLocalMemoryEntity = new ThreadLocalMemoryEntity();
				threadLocalMemoryEntity.setName("test" + i);
				//模拟发送请求 实际生产中每一个请求都会有一个线程
				SimulateReqPool.execute(() -> {
					threadLocal.set(threadLocalMemoryEntity);
					//模拟执行业务逻辑
					compensateBatchPool.execute(() -> {
						//模拟发送kafka消息
						try {
							Thread.sleep(100);
						} catch (InterruptedException e) {
							e.printStackTrace();
						}
						System.out.println("发送kafka数据:" + threadLocalMemoryEntity.getName());
					});

				});
			}
		} finally {
			compensateBatchPool.shutdown();
			SimulateReqPool.shutdown();

		}

	}
}

直接运行mian方法,结果如下
请添加图片描述
虽然GC会不断回收new的ThreadLocalMemoryEntity对象,但是由于不断将ThreadLocalMemoryEntity放入ThreadLocal中,导致内存溢出异常抛出。
如何防范ThreadLocal内存溢出
优化后的业务代码

public class ThreadLocalMemoryLeakExample {
	private static ThreadLocal<ThreadLocalMemoryEntity> threadLocal = ThreadPoolUtil.getThreadLocal();
	private static ThreadPoolExecutor compensateBatchPool = ThreadPoolUtil.getCompensateBatchPool();
	private static ThreadPoolExecutor SimulateReqPool = ThreadPoolUtil.getSimulateReqPool();

	public static void main(String[] args) {

		try {
			for (int i = 0; i < 10000; i++) {
				ThreadLocalMemoryEntity threadLocalMemoryEntity = new ThreadLocalMemoryEntity();
				threadLocalMemoryEntity.setName("test" + i);
				//模拟发送请求 实际生产中每一个请求都会有一个线程
				SimulateReqPool.execute(() -> {
					try {
						threadLocal.set(threadLocalMemoryEntity);
						//模拟执行业务逻辑
						compensateBatchPool.execute(() -> {
							//模拟发送kafka消息
							try {
								Thread.sleep(100);
							} catch (InterruptedException e) {
								e.printStackTrace();
							}
							System.out.println("发送kafka数据:" + threadLocalMemoryEntity.getName());
						});
					} finally {
						//防止内存泄露
						threadLocal.remove();
					}
				});
			}
		} finally {
			compensateBatchPool.shutdown();
			SimulateReqPool.shutdown();

		}

	}
}

注意需要在threadLocal.set 所在的线程 进行 remove才有效,因此在使用ThreadLocal的时候可以遵守这个编程规范

try {
			threadLocal.set(xxx);
		}finally {
			threadLocal.remove();
		}

总结

在多线程编程中,ThreadLocal和ThreadPoolExecutor是两个常用的工具类,但是它们也会带来内存泄露的风险。为了避免这种情况的发生,我们可以在使用完毕后手动删除变量或关闭线程池。希望本文能够对您有所帮助。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/751011.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【PHP面试题39】linux下面chmod和chown使用详解

文章目录 一、前言二、什么是 chmod 命令&#xff1f;2.1 使用方法&#xff1a;2.2 数值表示法&#xff1a;2.3 符号表示法&#xff1a; 三、什么是 chown 命令&#xff1f;3.1 使用方法&#xff1a;3.2 更改所有者和用户组&#xff1a; 四、使用示例4.1 使用 chmod 命令修改权…

媒体邀约:企业新品发布会如何邀约记者到现场采访报道?

媒介易是国内领先的全媒体广告营销平台&#xff0c;专注全媒体营销平台创新服务。我们有超过近11年的实战经验&#xff0c;我们拥有丰富的媒体记者资源&#xff0c;关于邀约记者到现场采访&#xff0c;我们会采取以下步骤&#xff1a; 1、提前策划&#xff1a;在发布会前至少…

MATLAB 基于NDT的点云配准实验(不同参数效果) (25)

MATLAB 基于NDT的点云配准实验(不同参数效果) (25) 一、算法简介二、具体使用1.代码(注释详细)2.结果(不同参数 与ICP比较)一、算法简介 NDT点云配准与ICP一样,都是经典的点云配准算法,这里使用MATLAB进行ndt点云配准,对配准结果进行显示,并尝试不同参数,得到较好…

单元测试用例到底该如何设计?

目录 前言 使用参数方法创建测试用例 使用执行路径方法创建测试用例 总结 前言 最近一些大公司在进行去测试化的操作&#xff0c;这一切的根源大概可以从几年前微软一刀切砍掉所有内部正式的测试人员开始说起&#xff0c;当时微软内部的测试工程师有一部分转职成了开发工程…

STM32 Proteus仿真红外检测PWM调速温控风扇-0073

STM32 Proteus仿真红外检测PWM调速温控风扇-0073 Proteus仿真小实验&#xff1a; STM32 Proteus仿真红外检测PWM调速温控风扇-0073 功能&#xff1a; 硬件组成&#xff1a;STM32F103C6单片机 LCD1602显示器DS18B20温度传感器人检测 按下说明有人L298驱动电机模拟风扇 1.按键…

PWM呼吸灯设计

呼吸灯&#xff1a; 呼吸灯是一种特殊的灯光效果&#xff0c;它可以模拟呼吸的效果&#xff0c;即灯光逐渐由暗变亮再由亮变暗&#xff0c;循环往复。这种效果给人一种柔和、舒缓的感觉&#xff0c;常被应用在装饰、照明和显示等领域。 PWM呼吸灯设计&#xff1a; 在数字电路设…

Windows搭建Nginx实现RTMP转为HLS流

所需软件 nginx-1.7.11.3-Gryphon&#xff08;这个包含必须的RTMP模块&#xff0c;普通的Ngxin没有这个&#xff09;ffmpegVLC 配置Nginx 1为Nginx配置RTMP和HLS 这里定义了一个叫live的RTMP路径。同时设置其开启HLS功能&#xff0c;那么所有推送到这个地址的RTMP流都会自动生…

AWS MSK集群认证和加密传输的属性与配置

通常&#xff0c;身份认证和加密传输是两项不相关的安全配置&#xff0c;在Kafka/MSK上&#xff0c;身份认证和加密传输是有一些耦合关系的&#xff0c;重点是&#xff1a;对于MSK来说&#xff0c;当启用IAM, SASL/SCRAM以及TLS三种认证方式时&#xff0c;TLS加密传输是必须的&…

C++STL库中的string

文章目录 STL库对于string类的介绍 string常用接口 string类的模拟实现 string对象大小的计算 写时拷贝 前言 C语言中&#xff0c;字符串是以\0结尾的一些字符的集合&#xff0c;为了操作方便&#xff0c;C标准库中提供了一些str系列的库函数&#xff0c;但是这些库函数与字…

青龙面板集合仓库(不断更新)青龙面板,京东定时任务库,脚本库大全

文章目录 文章目录前言简易一键安装脚本库最新京东比价小插件 文章目录 前言 Faker维护仓库&#xff0c;本地sign保证CK安全防泄漏&#xff0c;收集全网目前能正常使用的脚本。 全网能用的&#xff0c;本仓库都有。有问题进群反馈。 简易一键安装 最新版青龙有可能造成脚本…

软件测试技能提升自学,如何学?

又到了年底&#xff0c;对于我们测试同学来说&#xff0c;多多少少会立一些flag。我已经被连续打脸了好几年&#xff0c;生活为什么总是这么不易&#xff1f;好了&#xff0c;不扯远了&#xff0c;我们今天的主题是自学的那些事。 学习新的技能我相信是每一个测试同学都要面对…

使用MFC CAD 的一些使用方式记录【追加ing】

1. 项目调试&#xff1a;由于项目很大&#xff0c;因此&#xff0c;我们调试的时候&#xff0c;不应该编译整个软件而是应该只编译对应的 类去做处理 2. debug 设置断点方面&#xff1a; 以往我们的操作都是在.exe直接执行文件上进行操作&#xff0c;但是&#xff0c;现在&am…

解决虚拟机安装debian系统报错The failing step is: Select and install software

一波三折&#xff0c;一直卡在这一步&#xff0c;总是到不了流行度调查以及选择软件的界面。 下面是最终正确的步骤&#xff1a; 1&#xff0c;镜像源选项选择yes&#xff1b; 2&#xff0c;镜像源地区选择china&#xff1b; 3&#xff0c;镜像源地址可选mirrors.163.com&am…

Linux离线安装Jenkins、Maven、Gitlab、Git,部署Java项目

安装Java 《Linux安装java》 安装Maven 把Maven上传到Linux服务器/data/目录下进行解压 cd /data/ && tar -zxvf apache-maven-3.9.3-bin.tar.gz配置环境变量 vim /etc/profile找到export PATH USER LOGNAME MAIL HOSTNAME HISTSIZE HISTCONTROL在下面追加 # mave…

【C语言】约分最简式

题目描述&#xff1a; 分数可以表示为分子/分母的形式。编写一个程序&#xff0c;要求用户输入一个分数&#xff0c;然后将其约分为最简分式。最简分式是指分子和分母不具有可以约分的成分了。如6/12可以被约分为1/2。当分子大于分母时&#xff0c;不需要表达为整数又分数的形…

基于 JIT 技术的开源全场景高性能 JSON 库

大家好&#xff0c;我是Mandy&#xff0c;上一节我们对Go中的切片数据类型进行了深度的剖析&#xff0c;今天给大家分享一个字节跳动自研开源的JSON数据解析包。一个速度奇快的 JSON 序列化/反序列化库&#xff0c;由 JIT &#xff08;即时编译&#xff09;和 SIMD &#xff08…

基于亚博K210开发板——LED(RGB)点灯

文章目录 开发板实验目的实验准备查看原理图软件对应SDKGPIO配置函数什么是 FPIOA 呢 实验代码LED/RGB驱动主程序控制 实验结果 开发板 实验目的 实现开发板上LED0、LED1以及RGB灯的点亮 实验准备 查看原理图 K210 开发板出厂默认已经焊接好 LED0 和 LED1。LED0 连接的是 IO…

Acrel-1000DP分布式光伏系统在重工企业的应用

安科瑞 崔丽洁 摘 要&#xff1a;分布式光伏发电特指在用户场地附近建设&#xff0c;运行方式以用户侧自发自用、余电上网&#xff0c;且在配电系统平衡调节为特征的光伏发电设施&#xff0c;是一种新型的、具有广阔发展前景的发电和能源综合利用方式&#xff0c;它倡导就近发电…

2D、3D机器视觉各有优势与局限,融合应用将成工业领域生产新方式

在智能制造的浪潮中&#xff0c;制造行业生产线亟需转型升级&#xff0c;为国内机器视觉市场释放出了惊人的机器视觉技术及产品需求。在自动化工业质量控制和在线检测领域&#xff0c;2D机器视觉与3D机器视觉都具有重要的作用。那在机器视觉自动化场景中该如何选择合适的机器视…

udp传输大数据的原理和相关问题注意事项

UDP协议本身不提供大数据传输的分片、重组、丢包重传等功能&#xff0c;因此需要对大数据传输进行特殊处理。以下是UDP传输大数据一些常见的处理方法。 &#xff08;1&#xff09;对大文件进行分块&#xff1a;将大文件划分为多个小块进行传输&#xff0c;每个小块都可以使用UD…