资料来源:https://juejin.cn/post/7105968458851942414
一般队列要么指定队列长度,要么无界;
指定长度,队列中元素有的大,有的小,按数量来,不能保证需要占用多少内存;
无界队列,随便装,指不定就把内存撑爆了。
介绍2个内存相对比较安全的队列,由系统内存来控制队列中的元素数量,这个思路很有意思。
MemoryLimitedLinkedBlockingQueue
限制内存的队列
每次放入元素时,先检查该元素占用内存的大小(使用Instrumentation 这个类来获取元素大小),并且队列内部用一个字段来存储所有已放入的元素的总大小,每次放入前都先判断是否超过了限定的内存大小,如果超过了就等待,先不放入队列,直到队列中有元素被移除,释放了被移除元素占用的空间再放入队列,以此来防止oom。
缺点:
通过Instrumentation来获取元素大小,只能获取元素本身占用的大小,元素引用的就不能正确获取了。
Instrumentation是个接口,它的实现类InstrumentationImpl构造方法又是private的,不能自己实例化,不会用也挺麻烦的,
MemorySafeLinkedBlockingQueue
内存安全的队列
队列内部定义一个变量叫最大剩余内存,默认值是256M;往队列中放入新元素时,先会检查系统的剩余内存(通过ManagementFactory 里面的 MemoryMXBean来获取),如果系统剩余内存低于设定的最大剩余内存就不再添加,直接丢弃,因此队列始终不会占满内存,以此防止oom。
代码复制出来后看了下,发现获取剩余内存不是通过MemoryMXBean了,而是通过Runtime.getRuntime().freeMemory();来的,然后通过定时任务每隔50ms刷新一下。
缺点:
ManagementFactory 的 MemoryMXBean是查看当前堆内存的实际占用,但是没有考虑GC的影响,可能当前内存不够,但是也许内存大部分都是能回收的,回收后就有足够的内存了。我觉得这个问题不大。
这2个队列不是JDK里面的,是提交在dubbo里面,当时呢这2个类又不依赖dubbo,所以把这两个类直接复制出来就能用。
MemoryLimitedLinkedBlockingQueue :
https://github.com/apache/dubbo/pull/9722/files
MemorySafeLinkedBlockingQueue :
https://github.com/apache/dubbo/pull/10021/files
第一个链接里面的代码复制出来后注意 MemoryLimiter这个类里面有bug,在第二个链接里面对这个bug进行了修改。
如果以后有遇到用Map或其他容器做本地缓存的也可以借鉴这个思路,做一个限定内存的容器。