[心缘地方]同学录
首页 | 功能说明 | 站长通知 | 最近更新 | 编码查看转换 | 代码下载 | 常见问题及讨论 | 《深入解析ASP核心技术》 | 王小鸭自动发工资条VBA版
登录系统:用户名: 密码: 如果要讨论问题,请先注册。

[备忘]netty的DirectByteBuffer备忘

上一篇:[备忘]java缓存id范围,并发获取只有一个执行。
下一篇:[备忘]postgresql缓存参数

添加日期:2021/9/14 11:50:00 快速返回   返回列表 阅读642次
原文:
https://blog.csdn.net/liuxiao723846/article/details/89814005

一句话,JVM没GC,可能导致堆外内存无法释放。

ByteBuffer堆外内存回收的矛盾点:

我们知道java代码无法强制JVM何时进行垃圾回收,也就是说垃圾回收这个动作的触发,完全由JVM自己控制,它会挑选合适的时机回收堆内存中的无用java对象。代码中显示调用System.gc(),只是建议JVM进行垃圾回收,但是到底会不会执行垃圾回收是不确定的,一般来说是,系统比较空闲的时候(比如JVM中活动的线程很少的时候),或是内存不足,才进行垃圾回收。

使用ByteBuffer堆外内存回收的矛盾在于:堆内存由JVM自己管理,堆外内存必须要由我们自己释放;堆内存的消耗速度远远小于堆外内存的消耗,但要命的是必须先释放堆内存中的对象(引用),才能释放堆外内存,但是我们又不能强制JVM释放堆内存。例如:ByteBuffer bb = ByteBuffer.allocateDirect(1024),这段代码的执行会在堆外占用1k的内存,Java堆内只会占用一个对象的指针引用的大小,堆外的这1k的空间只有当bb对象被回收时,才会被回收,这里会发现一个明显的不对称现象,就是堆外可能占用了很多,而堆内没占用多少,导致还没触发GC,那就很容易出现Direct Memory造成物理内存耗光。

再者,假设堆内 ByteBuffer对象的引用升级到了老年代,导致这个引用会长期存在无法回收,这时堆外的内存将长期无法得到回收。
---------------------------------------------

如果空间不足,会调用System.gc()尝试释放内存,然后再进行判断,如果还是没有足够的空间,抛出OOME。
确定有足够的空间后,使用sun.misc.Unsafe#allocateMemory申请内存;
最后,DirectByteBuffer使用Cleaner机制进行空间回收

说明:
sun.misc.Unsafe.allocateMemory这个函数是通过JNI调用C的malloc来申请内存;
申请内存时,可以通过-XX:+PageAlignDirectMemory:指定申请的内存是否需要按页对齐,默认不对其;
默认堆外内存大小为可用的最大Java堆大小(后面会讲到)
————————————————
堆外内存:
DirectByteBuffer 最容易造成 OOM 的情况,DirectByteBuffer 对象的回收需要依赖 Old GC 或者 Full GC 才能触发清理。如果长时间没有 Old GC 或者 Full GC 执行,那么堆外内存即使不再使用,也会一直在占用内存不释放。我们最好通过 JVM 参数 -XX:MaxDirectMemorySize 指定堆外内存的上限大小,当堆外内存的大小超过该阈值时,就会触发一次 Full GC 进行清理回收,如果在 Full GC 之后还是无法满足堆外内存的分配,那么程序将会抛出 OOM 异常
 

评论 COMMENTS
没有评论 No Comments.

添加评论 Add new comment.
昵称 Name:
评论内容 Comment:
验证码(不区分大小写)
Validation Code:
(not case sensitive)
看不清?点这里换一张!(Change it here!)
 
评论由管理员查看后才能显示。the comment will be showed after it is checked by admin.
CopyRight © 心缘地方 2005-2999. All Rights Reserved