Java线程池动态内存队列思路

news2024/12/26 21:27:22

背景

在我们定义线程池时候,需要创建一个对列用来存储未执行而排队的任务,这个队列长度问题一直都是需要开发人员斟酌考虑点。在阿里巴巴开发手册中有怎么一个规则如:
说明: Executors返回的线程池对象的弊端如下

  1. FixedThreadPool和SingleThreadPool :允许的请求队列长度为Integer.MAX_VALUE,可能会堆积大量的请求,从而导致OOM
    根据上面阿里规范中为什么不建议使用 FixedThreadPool 和 SingleThreadPool 呢?
    因为队列太长了,请求会堆积,请求一堆积,容易造成 OOM。
    那么问题又来了前面提到的线程池用的队列是什么队列呢?用的是没有指定长度的 LinkedBlockingQueue。没有指定长度,默认长度是 Integer.MAX_VALUE,可以理解为无界队列了。所以,在我的认知里面,使用 LinkedBlockingQueue 是可能会导致 OOM 的。如果想避免这个 OOM 就需要在初始化的时候指定一个合理的值。下面我针对这个合理的值进行开展设计思路。

内存限制队列思路(MemoryLimitedLBQ)
引入思路案例:

https://github.com/apache/dubbo/pull/9722
![[Pasted image 20240730171554.png]]

这个类从命名上也看得出来,也是一个 LinkedBlockingQueue,但是它的限定词是 MemoryLimited,可以限制内存的。

内存安全队列思路(MemorySafeLBQ)
引入思路案例

https://github.com/apache/dubbo/pull/10021
![[Pasted image 20240730170018.png]]

这个PR描述如下:

可以完全解决因为 LinkedBlockingQueue 造成的 OOM 问题,而且不依赖 instrumentation,比 MemoryLimitedLinkedBlockingQueue 更好用。
![[Pasted image 20240730170440.png]]

下面我们来深入了解一下MemoryLimitedLinkedBlockingQueue和MemorySafeLinkedBlockingQueue应用场景和解决问题。

MemoryLimitedLinkedBlockingQueue的实现原理是什么?

从字面意思理解就是内存限制队列,在 Dubbo 的 pr中了解到信息是:但是它本质上是一个队列的实现方式,并完全脱离与框架而存在。具体文件如:> https://github.com/apache/dubbo/pull/9722/files
我先给你看看 MemoryLimitedLBQ 这个类,它就是继承自 LinkedBlockingQueue,然后重写了它的几个核心方法。只是自定义了一个 memoryLimiter 的对象,然后每个核心方法里面都操作了 memoryLimiter 对象:
![[Pasted image 20240730171959.png]]

所以真正的秘密就藏在 memoryLimiter 对象里面。看看这个 put 方法:
![[Pasted image 20240730172040.png]]

这里面调用了 memoryLimiter 对象的 acquireInterruptibly 方法。在解读 acquireInterruptibly 方法之前,我们先关注一下它的几个成员变量:
![[Pasted image 20240730172100.png]]

  • memoryLimit 就是表示这个队列最大所能容纳的大小。
  • memory 是 LongAdder 类型,表示的是当前已经使用的大小。
  • acquireLock、notLimited、releaseLock、notEmpty 是锁相关的参数,从名字上可以知道,往队列里面放元素和释放队列里面的元素都需要获取对应的锁。
  • inst 这个参数是 Instrumentation 类型的。

Instrumentation参数解释:
这玩意日常开发中基本上用不上,但是用好了,这就是个黑科技了。很多工具都是基于这个玩意来实现的,比如大名鼎鼎的 Arthas。

它可以更加方便的做字节码增强操作,允许我们对已经加载甚至还没有被加载的类进行修改的操作,实现类似于性能监控的功能。
可以说 Instrumentation 就是 memoryLimiter 的关键点。比如在 memoryLimiter 的 acquireInterruptibly 方法里面,它是这样的用的:
![[Pasted image 20240730173740.png]]

看方法名称你也知道了,get 这个 object 的 size,这个 object 就是方法的入参,也就是要放入到队列里面的元素。
![[Pasted image 20240730172629.png]]

an implementation-specific approximation of the amount of storage consumed by the specified object

整句话翻译过来就是:返回指定对象所消耗的存储量的一个特定实现的近似值。
再说的直白点就是你传进来的这个对象,在内存里面到底占用了多长的长度,这个长度不是一个非常精确的值。

MemorySafeLinkedBlockingQueue的实现原理是什么?

MemorySafeLinkedBlockingQueue 还是继承自 LinkedBlockingQueue,只是多了一个自定义的成员变量,叫做 maxFreeMemory,初始值是 256 * 1024 * 1024。
这个变量的名字就非常值得注意,你再细细品品。maxFreeMemory,最大的剩余内存,默认是 256M。

前面一节讲的 MemoryLimitedLinkedBlockingQueue 限制的是这个队列最多能使用多少空间,是站在队列的角度。

而 MemorySafeLBQ 限制的是 JVM 里面的剩余空间。比如默认就是当整个 JVM 只剩下 256M 可用内存的时候,再往队列里面加元素我就不让你加了。
因为整个内存都比较吃紧了,队列就不能无限制的继续添加了,从这个角度来规避了 OOM 的风险。

另外,它说它不依赖 Instrumentation 了,那么它怎么检测内存的使用情况呢?![[Pasted image 20240730174050.png]]

使用的是 ManagementFactory 里面的 MemoryMXBean。
这些信息就是从JConsole控制台 ManagementFactory 里面拿出来的:
![[Pasted image 20240730174111.png]]

所以,确实它没有使用 Instrumentation,但是它使用了 ManagementFactory。目的都是为了获取内存的运行状态。

MemorySafeLinkedBlockingQueue为什么比MemoryLimitedLinkedBlockingQueue更好用?

关键方法就是这个 hasRemainedMemory,在调用 put、offer 方法之前就要先调用这个方法:
![[Pasted image 20240730174318.png]]MemorySafeLinkedBlockingQueue 只是重写了放入元素的 put、offer 方法,并不关注移除元素。
因为它的设计理念是只关心添加元素时候的剩余空间大小,它甚至都不会去关注当前这个元素的大小。
所以我们看看 MemoryLimitCalculator 的源码。
![[Pasted image 20240730174504.png]]第一行是调用 refresh 方法,也就是对 maxAvilable 这个参数进行重新赋值,这个参数代表的意思是当前还可以使用的 JVM 内存。

第二行是注入了一个每 50ms 运行一次的定时任务。到点了,就触发一下 refresh 方法,保证 maxAvilable 参数的准实时性。

第三行是加入了 JVM 的 ShutdownHook,停服务的时候需要把这个定时任务给停了,达到优雅停机的目的。

思路扩展

场景演练:
项目里面用 Map 来做本地缓存,就会放很多元素进去,也会有 OOM 的风险!
解决方案
我们可以参考安全内存队列的思路解决避规OOM风险。

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

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

相关文章

【前端面试】七、算法-递归

遍历方法总结 链式调用 数组的很多操作可以构成链式操作,类似这样的格式:…map().filter(…).sort(…).map(….)链式操作就是对象方法返回类型是自身的。比如map是属于数组的方法,它返回数组,所以构成了链式操作优势:…

【iOS】——持久化

在iOS开发中,数据持久化是非常重要的,因为它允许应用程序在不同会话之间保存用户数据、设置、偏好等信息。 为什么数据持久化 数据保存: 目的:将应用程序中的数据保存到非易失性存储中,以便在应用程序关闭或重启后仍…

眼镜清洗机哪个品牌好?性价比高的超声波眼镜清洗机

清洁眼镜、化妆刷、项链等物品其实是挺麻烦的,尤其是化妆刷这种经常使用的物品,需要用专门的清洁剂并保持一定的清洗频率。眼镜的日常清洁主要是用眼镜布擦拭镜片上的灰尘和指纹,但对于顽固的污渍或油脂,只有超声波清洗机能提供快…

六西格玛管理法

六西格玛管理法是一种旨在提高业务流程效率和减少缺陷的管理策略。它最初由摩托罗拉公司在1980年代末期提出,并随后被通用电气等公司广泛应用和发展。六西格玛的核心理念是通过减少过程变异性来提高产品质量和服务水平。 六西格玛的含义: 统计学概念&am…

一款功能全面的卸载工具,强大,免费,小巧

HiBit Uninstaller是一款功能全面的卸载工具,它不仅可以卸载Windows程序,还提供了诸如注册表清理、垃圾文件清理等多种系统优化功能。该软件以其小巧、强大、免费的特点受到用户的欢迎,尤其适合处理顽固软件和流氓程序的卸载问题。 主要功能…

WPF的MVVM架构:如何通过数据绑定简化UI逻辑

WPF的MVVM架构:如何通过数据绑定简化UI逻辑 目录 MVVM模式概述数据绑定在MVVM中的作用实现MVVM模式的步骤MVVM模式中的常见问题与解决方案实践示例总结 MVVM模式概述 MVVM(Model-View-ViewModel)是一种设计模式,用于WPF应用程序…

机器学习(五) -- 无监督学习(2) --降维1

系列文章目录及链接 上篇:机器学习(五) -- 无监督学习(1) --聚类2 下篇:机器学习(五) -- 无监督学习(2) --降维2 前言 tips:标题前有“***”的内…

热门超声波清洗机有哪些?小型超声波清洗机推荐

在繁忙的工作和生活中,许多人常常会因为种种原因忽略日常的小事,比如忘记清洁手表、眼镜、首饰等常用物品。实际上,这些物品表面不仅积累了灰尘和污垢,特别是跟眼部朝夕相处的眼镜,还可能滋生各种致病细菌,…

Vue3-如何自己写一个“返回顶部”功能

功能描述: 在屏幕的右下角固定一个“返回顶部”按钮,只有当用户滚动屏幕一定程度后出现,否则隐藏。 点击按钮,网页平滑的滚动到页面顶部。 环境:Vue3,js,antd 具体思路: 1、给窗口挂载滚动事…

Python 学习中的 API,如何调用API ?

1.1 API的定义 API,全称是Application Programming Interface(应用程序编程接口)。它是一组定义好的协议和工具,用于在软件应用程序之间进行通信。API可以简化软件开发,使不同的应用程序能够相互协作。它是软件开发中…

阿里云服务器 Ubuntu18.04 安装 mysql8.0并允许外部连接

参考教程: 官网教程 参考教程一 首先彻底删除mysql5.7 dpkg --list|grep mysql #查看 sudo apt-get remove mysql-common #卸载 sudo apt-get autoremove --purge mysql-server-5.7 #版本自己修改 dpkg -l|grep ^rc|awk {print$2}|sudo xargs dpkg -P #清除残留数…

LeetCode Hot100 将有序数组转换为二叉搜索树

给你一个整数数组 nums ,其中元素已经按 升序 排列,请你将其转换为一棵 平衡 二叉搜索树。 示例 1: 输入:nums [-10,-3,0,5,9] 输出:[0,-3,9,-10,null,5] 解释:[0,-10,5,null,-3,null,9] 也将被视为正确…

电商老司机教您批量下载1688高清主图、详情图、sku及视频信息

图片在电商中至关重要,高质量的商品图片能吸引顾客注意,提升购买欲望。它们是展示商品特性和细节的主要方式,有助于增强消费者信任,减少退换货率。好的图片还能优化搜索排名,提高转化率。简而言之,图片是电…

Luma AI的战略转向:从Nerf到视频生成领域的背后故事

引言 今天我们将深入探讨Luma AI近期引发关注的视频生成模型——Dream Machine。Luma AI从最初的3D重建和生成业务逐步转向视频生成领域的背后,隐藏着什么样的战略考量和技术演进?让我们通过Luma AI首席科学家宋佳铭的最新访谈,揭开这场技术…

【每日一题 | 数据结构】时间复杂度计算

题目 解题方法 对于二重循环求时间复杂度: 写出外层i的变化值写出内层循环语句执行次数(看j)对次数求和找到频度和n的关系 笔记 视频跳转: 【每日一题 | 数据结构】时间复杂度计算

手写操作系统:二级引导程序

项目简介 在上篇博客,我们完成了主引导扇区的编写,在主引导扇区我们初始化了寄存器,加载了二级引导程序到内存地址 0x8000处,并跳转至0x8000处执行,在本文我们将继续编写二级引导程序。 在二级引导程序将完成以下任务…

Unity UGUI 实战学习笔记(6)

仅作学习,不做任何商业用途 不是源码,不是源码! 是我通过"照虎画猫"写的,可能有些小修改 不提供素材,所以应该不算是盗版资源,侵权删 因为注册和登录面板的逻辑与数据存储方面已经相对完善 服务器面板逻辑…

为什么现在的家具很多带缓冲器?

在当今的家具市场中,我们不难发现,很多的家具配备了缓冲器。这一现象的背后,有着多方面的原因。首先,随着人们生活水平的提高,对于生活品质的追求也日益增强。缓冲器能够有效地减少家具关闭时产生的噪音,为…

如何通过✅ IPIDEA代理IP,轻松实现数据采集和市场拓展工作(下)

如何通过✅ IPIDEA代理IP,轻松实现数据采集和市场拓展工作 如何通过✅ IPIDEA代理IP,轻松实现数据采集和市场拓展工作前言IPIDEA爬虫实战实战Demo演示总结 如何通过✅ IPIDEA代理IP,轻松实现数据采集和市场拓展工作 前言 在当今全球化市场的…

【Qt】QTextEdit

QTextEdit是Qt中用于编辑和显示文本内容的类。其提供了丰富的用户界面控件,可以用于创建和包含格式化文本、图片和链接的文本编辑器 常用属性 属性说明markdown输入框内持有的内容。支持markdown格式,能自动的对markdown文本进行渲染成htmlhtml输入框持…