Java 堆外内存
入口ByteBuffer.allocateDirect
public static ByteBuffer allocateDirect(int capacity) {
return new DirectByteBuffer(capacity);
}
DirectByteBuffer构造函数
DirectByteBuffer(int cap) { // package-private
super(-1, 0, cap, cap);
boolean pa = VM.isDirectMemoryPageAligned();
int ps = Bits.pageSize();
long size = Math.max(1L, (long)cap + (pa ? ps : 0));
Bits.reserveMemory(size, cap);
long base = 0;
try {
base = unsafe.allocateMemory(size);
} catch (OutOfMemoryError x) {
Bits.unreserveMemory(size, cap);
throw x;
}
unsafe.setMemory(base, size, (byte) 0);
if (pa && (base % ps != 0)) {
// Round up to page boundary
address = base + ps - (base & (ps - 1));
} else {
address = base;
}
cleaner = Cleaner.create(this, new Deallocator(base, size, cap));
att = null;
}
JDK中使用DirectByteBuffer对象来表示堆外内存,每个DirectByteBuffer对象在初始化时,都会创建一个对用的Cleaner对象,这个Cleaner对象会在合适的时候执行
unsafe.freeMemory(address),从而回收这块堆外内存。当初始化一块堆外内存时,对象的引用关系如下:
其中first是Cleaner类的静态变量,Cleaner对象在初始化时会被添加到Clener链表中,和first形成引用关系,ReferenceQueue是用来保存需要回收的Cleaner对象。
如果该DirectByteBuffer对象在一次GC中被回收了
此时,只有Cleaner对象唯一保存了堆外内存的数据(开始地址、大小和容量),在下一次Full GC时,把该Cleaner对象放入到ReferenceQueue中,并触发clean方法。
1. 堆外内存的创建
在DirectByteBuffer中,首先向Bits类申请额度,Bits类有一个全局的 totalCapacity变量,记录着全部DirectByteBuffer的总大小,每次申请,都先看看是否超限,
堆外内存的限额默认与堆内内存(由-Xmx 设定)相仿,可用 -XX:MaxDirectMemorySize 重新设定。如果已经超限,会主动执行Sytem.gc(),期待能主动回收一点堆外内存。
然后休眠一百毫秒,看看totalCapacity降下来没有,如果内存还是不足,就抛出OOM异常。如果额度被批准,就调用大名鼎鼎的sun.misc.Unsafe去分配内存。
2. 堆外内存基于GC的回收
存在于堆内的DirectByteBuffer对象很小,只存着基地址和大小等几个属性,和一个Cleaner,但它代表着后面所分配的一大段内存,是所谓的冰山对象。
快速回顾一下堆内的GC机制,当新生代满了,就会发生young gc;如果此时对象还没失效,就不会被回收;撑过几次young gc后,对象被迁移到老生代;
当老生代也满了,就会发生full gc。这里可以看到一种尴尬的情况,因为DirectByteBuffer本身的个头很小,只要熬过了young gc,即使已经失效了也能
在老生代里舒服的呆着,不容易把老生代撑爆触发full gc,如果没有别的大块头进入老生代触发full gc,就一直在那耗着,占着一大片堆外内存不释放。
这时,就只能靠前面提到的申请额度超限时触发的system.gc()来救场了。但这道最后的保险其实也不很好,首先它会中断整个进程,然后它让当前线程
睡了整整一百毫秒,而且如果gc没在一百毫秒内完成,它仍然会无情的抛出OOM异常。还有,万一,万一大家迷信某个调优指南设置了-DisableExplicitGC
禁止了system.gc(),那就不好玩了。所以,堆外内存还是自己主动点回收更好,比如Netty就是这么做的。
3. 堆外内存的主动回收
对于Sun的JDK这其实很简单,只要从DirectByteBuffer里取出那个sun.misc.Cleaner,然后调用它的clean()就行。
在Netty里,因为不确定跑在Sun的JDK里(比如安卓),所以多废了些功夫来确定Cleaner的存在。
4. Cleaner如何与GC相关联?
Cleaner就是PhantomReference的子类。
当GC时发现它除了PhantomReference外已不可达(持有Cleaner的DirectByteBuffer失效了),就会把它放进 Reference类pending list静态变量里。
然后另有一条ReferenceHandler线程,关注着这个pending list,如果看到有对象类型是Cleaner,就会执行它的clean(); 如果是其他类型就放入ReferenceQueue中,
这样应用的代码可以从Queue里拖出这些理论上已死的对象,这是一种比finalizer更轻量更好的机制。
参考:
Are Java DirectByteBuffer wrappers garbage collected?
占小狼 : DirectByteBuffer
江南白衣: 堆外内存
Java 堆外内存的更多相关文章
- google-perftools 分析JAVA 堆外内存
google-perftools 分析JAVA 堆外内存 分类: j2se2011-08-25 21:48 3358人阅读 评论(4) 收藏 举报 javahbasehtml工具os 原文转自:htt ...
- Java堆外内存管理
Java堆外内存管理 1.JVM可以使用的内存分外2种:堆内存和堆外内存: 堆内存完全由JVM负责分配和释放,如果程序没有缺陷代码导致内存泄露,那么就不会遇到java.lang.OutOfMemo ...
- Java堆外内存之二:堆外内存使用总结
目录: <堆外内存操作类ByteBuffer> <DirectBuffer> <Unsafe(java可直接操作内存(),挂起与恢复,CAS操作)> 有时候对内存进 ...
- 实战经验 | Cassandra Java堆外内存排查经历全记录
背景 最近准备上线cassandra这个产品,同事在做一些小规格ECS(8G)的压测.压测时候比较容易触发OOM Killer,把cassandra进程干掉.问题是8G这个规格我配置的heap(Xmx ...
- 超干货!Cassandra Java堆外内存排查经历全记录
背景 最近准备上线cassandra这个产品,同事在做一些小规格ECS(8G)的压测.压测时候比较容易触发OOM Killer,把cassandra进程干掉.问题是8G这个规格我配置的heap(Xmx ...
- Java堆外内存的使用
堆外内存的回收见HeapByteBuffer和DirectByteBuffer以及回收DirectByteBuffer 基本类型长度 在Java中有很多的基本类型,比如: byte,一个字节是8位bi ...
- Netty之Java堆外内存扫盲贴
Java的堆外内存本来是高贵而神秘的东西,只在一些缓存方案的收费企业版里出现.但自从用了Netty,就变成了天天打交道的事情,毕竟堆外内存能减少IO时的内存复制,不需要堆内存Buffer拷贝一份到直接 ...
- Java堆外内存之突破JVM枷锁
对于有Java开发经验的朋友都知道,Java中不需要手动的申请和释放内存,JVM会自动进行垃圾回收:而使用的内存是由JVM控制的. 那么,什么时机会进行垃圾回收,如何避免过度频繁的垃圾回收?如果JVM ...
- Java堆外内存之五:堆外内存管理类ByteBuffer
本篇主要讲解如何使用直接内存(堆外内存),并按照下面的步骤进行说明: 相关背景-->读写操作-->关键属性-->读写实践-->扩展-->参考说明 希望对想使用直接内存的朋 ...
随机推荐
- Angular4学习笔记(一)-环境搭建
下载nodejs 下载地址 在命令行输入:npm -v 如果出现如下画面即安装成功 安装Angular的cli命令行工具 命令:sudo npm install -g @angular/cli 输入n ...
- Scala学习笔记(一):入门
变量定义 Scala有两种变量,val和var.val类似于Java中的final变量,一旦初始化了,val就不能再被赋值.var可以多次赋值.但由于函数式编程特性,Scala更推崇val. var的 ...
- linux swoole
swoole安装需要:linux7 +php5.3.10以上版本+gcc-4.4 或更高版本 下载地址: https://github.com/swoole/swoole-src/releases h ...
- MQ选型对比
现公司选择RocketMQ作为消息队列服务器,用于异步处理,应用解耦,流量削锋和消息通讯四个场景.RocketMQ特性参见:Rocketmq整体分析. PS: http://blog.csdn.net ...
- yum安装VirtualBox
参考官方文档: https://www.virtualbox.org/wiki/Linux_Downloads 配置yum源: vim /etc/yum.repos.d/virtualbox.repo ...
- VBA 根据Find方法根据特定内容查找单元格
http://club.excelhome.net/thread-940744-1-1.html 2. Find方法的语法[语法]<单元格区域>.Find (What,[After],[L ...
- mongodb new file allocation failure
话说那天正在向mongodb中写入数据,突然就蹦出了 new file allocation failure ,以为是数据有错误,就检查了一番,可没问题啊,看着像是mongo自己的问题,于是百度了一番 ...
- 终于等到你,最强 IDE Visual Studio 2017 正式版发布
Visual Studio 2017 正式版发布,该版本不仅添加了实时单元测试.实时架构依赖关系验证等新特性,还对许多实用功能进行了改进,如代码导航.IntelliSense.重构.代码修复和调试等等 ...
- 免费的SSL证书(LINUX)
贫穷限制了我的SSL. 说起来也简单,免费的SSL证书授权机构,我使用的是Certbot 选择服务器开启的服务,像我php之流,无非apache和nginx,然后选择使用的服务器类型.嗯,补充一句,这 ...
- [转] - Spark排错与优化
Spark排错与优化 http://blog.csdn.net/lsshlsw/article/details/49155087 一. 运维 1. Master挂掉,standby重启也失效 Mast ...