更多 Java 虚拟机方面的文章,请参见文集《Java 虚拟机》

为什么需要使用堆外内存

将长期存活的对象(如 Local Cache )移入堆外内存( off-heap,又名直接内存 direct-memory),从而减少 CMS 管理的对象数量, 以降低 Full GC 的次数和频率,达到提高系统响应速度的目的。

加快了复制的速度:堆内在 flush 到远程时,会先复制到直接内存,然后在发送;而堆外内存相当于省略掉了这个工作。

堆外内存不是 JVM 运行时数据区 Runtime Data Area 的一部分,这部分内存区域直接被操作系统管理,JVM 通过 JNI 本地接口操作堆外内存。

堆外内存的使用

在 JDK 1.4以前,对这部分内存访问没有光明正大的做法:只能通过反射拿到 Unsafe 类,然后调用allocateMemory()/freeMemory()来申请/释放这块内存。

1.4 开始新加入了 NIO,它引入了一种基于 Channel 与 Buffer 的 I/O 方式,可以使用 Native 函数库直接分配堆外内存,然后通过一个存储在 Java 堆里面的 DirectByteBuffer 对象作为这块内存的引用进行操作,ByteBuffer 提供了如下常用方法来跟堆外内存打交道:

public static ByteBuffer allocateDirect(int capacity)

分配堆外内存,返回一个 DirectByteBuffer 堆外内存对象 return new DirectByteBuffer(capacity);

public abstract ByteBuffer put(byte b);

向堆外内存中存放一个字节

public abstract byte get();

从堆外内存中读取一个字节

public final ByteBuffer put(byte[] src)

向堆外内存中存放一个字节数组

public ByteBuffer get(byte[] dst)

从堆外内存中读取一个字节数组

public abstract ByteBuffer putInt(int value);

向堆外内存中存放一个 int

public abstract int getInt();

从堆外内存中读取一个 int

public abstract IntBuffer asIntBuffer()

转换为一个 IntBuffer

public abstract ByteBuffer putLong(long value); 同上,以此类推

public abstract boolean isDirect();

判断是否为堆外内存

ByteBuffer 包含了如下的几个属性:

private int mark = -1;:标记位置,记录当前 position 的值

private int position = 0;:当前位置

private int limit;:限制大小

private int capacity;:空间容量

基本关系 mark <= position <= limit <= capacity

示例如下:

public static void main(String[] args) {

ByteBuffer bb = ByteBuffer.allocateDirect(1024);

bb.putChar('A');

bb.putInt(123);

System.out.println("capacity: " + bb.capacity());

System.out.println("limit: " + bb.limit());

System.out.println("position: " + bb.position());

bb.position(0);

System.out.println(bb.getChar());

System.out.println(bb.getInt());

}

输出:

capacity: 1024

limit: 1024

position: 6

A

123

堆外内存的设置

堆外内存的限额默认与堆内内存(由-XMX 设定)相仿,可用 -XX:MaxDirectMemorySize 重新设定。

当使用达到了阈值的时候将调用 System.gc 来做一次 Full GC,以此来回收掉没有被使用的堆外内存。

堆外内存的分配

在 DirectByteBuffer 中,首先向 Bits 类申请额度,Bits 类有一个全局的 totalCapacity 变量,记录着全部 DirectByteBuffer 的总大小,每次申请,都先看看是否超限:

如果已经超限,会主动执行 Sytem.gc(),期待能主动回收一点堆外内存。然后休眠一百毫秒,看看 totalCapacity 降下来没有,如果内存还是不足,就抛出大家最头痛的 OOM 异常。

如果额度被批准,就调用大名鼎鼎的 sun.misc.Unsafe 去分配内存,返回内存基地址,Unsafe 的 C++实现在此,标准的 malloc。然后再调一次 Unsafe 把这段内存给清零。

堆外内存的回收

堆外内存基于 GC 的回收

存在于堆内的 DirectByteBuffer 对象很小,只存着基地址和大小等几个属性,和一个 Cleaner,但它代表着后面所分配的一大段内存,是所谓的冰山对象。

通过前面说的 Cleaner,堆内的 DirectByteBuffer 对象被 GC 时,它背后的堆外内存也会被回收。

这里可以看到一种尴尬的情况,因为 DirectByteBuffer 本身的个头很小,只要熬过了 Young GC,即使已经失效了也能在老生代里舒服的呆着,不容易把老生代撑爆触发 Full GC,如果没有别的大块头进入老生代触发Full GC,就一直在那耗着,占着一大片堆外内存不释放。

这时,就只能靠前面提到的申请额度超限时触发的 System.gc()来救场了。

堆外内存的主动回收

对于 Sun 的 JDK 这其实很简单,只要从 DirectByteBuffer 里取出那个 sun.misc.Cleaner,然后调用它的 clean() 就行。

例如:

((DirectBuffer)bb).cleaner().clean();

java堆外内存6_Java 堆外内存的使用相关推荐

  1. java堆外内存6_Java堆外内存排查小结

    简介 JVM堆外内存难排查但经常会出现问题,这可能是目前最全的JVM堆外内存排查思路.之前的文章排版太乱,现在整理重发一下,内容是一样的. 通过本文,你应该了解: pmap 命令 gdb 命令 per ...

  2. java nio 堆外内存_Java堆外内存之突破JVM枷锁

    对于有Java开发经验的朋友都知道,Java中不需要手动的申请和释放内存,JVM会自动进行垃圾回收:而使用的内存是由JVM控制的. 那么,什么时机会进行垃圾回收,如何避免过度频繁的垃圾回收?如果JVM ...

  3. Java堆外内存:堆外内存溢出问题排查

    一.堆外内存组成 通常JVM的参数我们会配置 -Xms 堆初始内存  -Xmx 堆最大内存  -XX:+UseG1GC/CMS 垃圾回收器  -XX:+DisableExplicitGC 禁止显示GC ...

  4. java堆内存和堆外内存_Java堆空间,本机堆和内存问题

    java堆内存和堆外内存 最近,我正在和一个朋友讨论为什么Java进程使用的内存比启动Java进程时设置的最大堆多. 代码创建的所有Java对象都是在Java堆空间内创建的,其大小由-Xmx选项定义. ...

  5. java nio 李林峰_Netty堆外内存泄漏排查,这一篇全讲清楚了

    上篇文章介绍了Netty内存模型原理,由于Netty在使用不当会导致堆外内存泄漏,网上关于这方面的资料比较少,所以写下这篇文章,专门介绍排查Netty堆外内存相关的知识点,诊断工具,以及排查思路提供参 ...

  6. Java堆内内存和堆外内存

    1.堆内内存概念 堆内一般指堆内内存,英文全称:on-heap memory (heap:堆,java的内存区) java虚拟机分配非空对象一般就放到堆内内存,并且虚拟机会定期会进行垃圾回收在某些特定 ...

  7. 浅谈Java堆内内存、堆外内存、直接内存、用户空间和内核空间

    我们都知道Java中大多数的对象都存在于堆内存中,那什么是堆外内存.直接内存?它们又分别用来做什么?分布在用户空间还是内核空间? 首先,有个前置知识点,所谓的Java程序,其实可以理解为是用C/C++ ...

  8. 堆外内存与堆内内存详解

    堆外内存一直是Java业务开发人员难以企及的隐藏领域,究竟他是干什么的,以及如何更好的使用呢?那就请跟着我进入这个世界吧. 一.什么是堆外内存 1.堆内内存(on-heap memory)回顾 堆外内 ...

  9. Spark 内存管理堆内和堆外内存规划_大数据培训

    堆内和堆外内存规划 作为一个 JVM 进程,Executor 的内存管理建立在 JVM 的内存管理之上,Spark 对 JVM 的堆内(On-heap)空间进行了更为详细的分配,以充分利用内存.同时, ...

最新文章

  1. 目前python主要应用领域零售_python3读取HDA零售企业数据(一)
  2. 为什么redis是单线程的以及为什么这么快?
  3. python3循环遍历嵌套字典替换指定值
  4. 用Sql添加删除字段,判断字段是否存在的方法
  5. linux系统需要备份吗,准备好了吗?请备份你的Linux系统
  6. linux下编译安装ACE-6.5.1
  7. 用notepad++打造自己的C/C++语言IDE --是时候和DOS屏幕说再见了
  8. 机器学习算法太厉害,导致人类也通不过谷歌验证码
  9. 计算机网络基础应用课程标准,王建波《计算机网络基础》课程标准.doc
  10. 电脑不断弹窗的解决办法
  11. Capture CIS配置Mysql数据库连接
  12. matlab实现将彩色图像转换成灰色图像的方法
  13. android room 分页,Android官方ORM数据库Room技术解决方案简介(一)
  14. 为什么网上都说 AirPods 3 音质不如AirPods Pro?
  15. my visitor
  16. kettle发送邮件报错:535,authentication failed
  17. Windows Server 2012 R2安装域控制器完整版
  18. BEPU物理引擎碰撞系统的架构与设计
  19. hibernate QBC和QBE精讲与案列分析(下)
  20. PLSQL连接ORACLE报错ORA-12541:TNS:无监听程序

热门文章

  1. Android开机自动运行APP——BroadcastReceiver
  2. CentOS安装中文man手册
  3. 记录一次奇葩的sleep(15)引起的Too many connections
  4. Cassandra Leveled Compaction源码阅读
  5. 电脑账户与用户账户编码规则
  6. WinAPI: SetRectEmpty、IsRectEmpty
  7. screen的一些常用命令和快捷键
  8. 剑指 Offer II 044. 二叉树每层的最大值
  9. mysql简单部署_Docker部署简单的mysql
  10. linux mysql web界面吗_Linux下安装MySQL Web 管理工具phpMyAdmin