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

这篇具有很好参考价值的文章主要介绍了探究ThreadLocal和ThreadPoolExecutor中的内存泄露风险与防范策略。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

探究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方法,结果如下
探究ThreadLocal和ThreadPoolExecutor中的内存泄露风险与防范策略,性能优化,并发编程专题,java
虽然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是两个常用的工具类,但是它们也会带来内存泄露的风险。为了避免这种情况的发生,我们可以在使用完毕后手动删除变量或关闭线程池。希望本文能够对您有所帮助。文章来源地址https://www.toymoban.com/news/detail-563653.html

到了这里,关于探究ThreadLocal和ThreadPoolExecutor中的内存泄露风险与防范策略的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Java中的内存泄露、内存溢出与栈溢出

    大家好,我是欧阳方超。本次就Java中几个相似而又不同的概念做一下介绍。内存泄漏、内存溢出和栈溢出都是与内存相关的问题,但它们之间有所不同。 我们经常会遇到内存泄漏、内存溢出和栈溢出等问题,这些问题都与内存的使用有关。 内存泄漏(memory leak)指的是程序

    2024年02月03日
    浏览(72)
  • JavaScript中的数据缓存与内存泄露:解密前端性能优化与代码健康

    ​🌈个人主页:前端青山 🔥系列专栏:JavaScript篇 🔖 人终将被年少不可得之物困其一生 依旧 青山 ,本期给大家带来JavaScript篇专栏内容:JavaScript-数据缓存与内存泄露 目录 说说你对事件循环的理解 一、是什么 二、宏任务与微任务 微任务 宏任务 三、async与await async await 四、

    2024年02月03日
    浏览(76)
  • docker run密码泄露风险

    1.docker 2.docker inspect minio  采集措施: 尽量避免使用-e,显示声明环境参数

    2024年02月14日
    浏览(37)
  • ThreadLocal真会内存泄漏?

    在讨论ThreadLocal存在内存泄漏问题之前,需要先了解下面几个知识点: 什么是内存泄漏? 什么是ThreadLocal? 为什么需要ThreadLocal? 数据一致性问题 如何解决数据一致性问题? 当我们了解了上面的知识点以后,会带大家一起去了解真相。包括下面几个知识点: 为什么会产生内

    2024年04月11日
    浏览(84)
  • 你的服务器还安全吗?用户数据是否面临泄露风险?

    一系列严重的网络安全事件引起了广泛关注,多家知名公司的服务器遭到黑客挟持,用户的个人数据和敏感信息面临泄露的风险。这些事件揭示了网络安全的脆弱性和黑客攻击的威胁性,提醒着企业和个人加强对网络安全的重视。 蔚来数据泄露,被勒索225万美元等额比特币,

    2024年02月11日
    浏览(53)
  • ThreadLocal有内存泄漏问题吗

    对于ThreadLocal的原理不了解或者连Java中的引用类型都不了解的可以看一下我的之前的一篇文章Java中的引用和ThreadLocal_鱼跃鹰飞的博客-CSDN博客 我这里也简单总结一下: 1. 每个Thread里都存储着一个成员变量,ThreadLocalMap 2. ThreadLocal本身不存储数据,像是一个工具类,基于ThreadL

    2024年02月14日
    浏览(46)
  • ThreadLocal引发的内存泄漏分析

    Object o = new Object(); 这个o,我们可以称之为对象引用,而new Object()我们可以称之为在内存中产生了一个对象实例。 当写下  o=null 时,只是表示o不再指向堆中object的对象实例,不代表这个对象实例不存在了。 强引用:  就是指在程序代码之中普遍存在的,类似“Object obj=new Ob

    2024年02月09日
    浏览(53)
  • 被通报?被下架?用户信息被泄露?APP安全合规风险该如何防范?

    前段时间,一个资讯类APP(以下称“某APP”)的负责人急匆匆找到网安云,直言其负责的APP最近收到很多用户投诉,说他们的信息被泄露了,屡遭电销骚扰。由于电销太过猖狂,导致很多用户都到应用市场给他们发差评,对品牌形象塑造和业务发展影响极大!   同时,他们也

    2024年02月05日
    浏览(49)
  • ThreadLocal-为什么会导致内存泄漏

    一个Thread中存在字段为ThreadLocalMap ThreadLocal.ThreadLocalMap threadLocals = null; 也就是说我们使用threadLocal进行set操作和get操作,其实就是操作该Thread的threadLocalMap。 这个map是一个基于数组的key和value的格式,在进行get的时候,会将调用对象this作为key进行查询。 hashMap是通过链表法,

    2024年02月03日
    浏览(62)
  • 阿里二面:谈谈ThreadLocal的内存泄漏问题?问麻了。。。。

    ThreadLocal 在Java多线程编程中扮演着重要的角色,它提供了一种线程局部存储机制,允许每个线程拥有独立的变量副本,从而有效地避免了线程间的数据共享冲突。ThreadLocal的主要用途在于,当需要为每个线程维护一个独立的上下文变量时,比如每个线程的事务ID、用户登录信

    2024年03月24日
    浏览(59)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包