堆外内存相关(unsafe和nio包下的directByteBuffer)
发布日期:2021-07-20 20:53:38 浏览次数:61 分类:技术文章

本文共 2666 字,大约阅读时间需要 8 分钟。

为什么要使用堆外内存

DirectByteBuffer在创建的时候会通过Unsafe的native方法来直接使用malloc分配一块内存,这块内存是heap之外的,那么自然也不会对gc造成什么影响(System.gc除外),因为gc耗时的操作主要是操作heap之内的对象,对这块内存的操作也是直接通过Unsafe的native方法来操作的,相当于DirectByteBuffer仅仅是一个壳,还有我们通信过程中如果数据是在Heap里的,最终也还是会copy一份到堆外,然后再进行发送,所以为什么不直接使用堆外内存呢。对于需要频繁操作的内存,并且仅仅是临时存在一会的,都建议使用堆外内存,并且做成缓冲池,不断循环利用这块内存。

为什么不能大面积使用堆外内存

如果我们大面积使用堆外内存并且没有限制,那迟早会导致内存溢出,毕竟程序是跑在一台资源受限的机器上,因为这块内存的回收不是你直接能控制的,当然你可以通过别的一些途径,比如反射,直接使用Unsafe接口等,但是这些务必给你带来了一些烦恼,Java与生俱来的优势被你完全抛弃了---开发不需要关注内存的回收,由gc算法自动去实现。另外上面的gc机制与堆外内存的关系也说了,如果一直触发不了cms gc或者full gc,那么后果可能很严重。

DirectByteBuffer通常用在通信过程中做缓冲池,在mina,netty等nio框架中屡见不鲜,

 

原链接:

其他知识:

堆内内存:

堆内内存 = 新生代+老年代+持久代

 

1.    堆内存完全由JVM负责分配和释放,如果程序没有缺陷代码导致内存泄露,那么就不会遇到java.lang.OutOfMemoryError这个错误。

使用堆外内存,就是为了能直接分配和释放内存,提高效率。JDK5.0之后,代码中能直接操作本地内存的方式有2种

1.使用未公开的Unsafe。

2.NIO包下ByteBuffer。java.nio.DirectByteBuffer对象进行堆外内存的管理和使用,它会在对象创建的时候就分配堆外内存。

 

堆外内存的好处是:

1、可以扩展至更大的内存空间。比如超过1TB甚至比主存还大的空间。 

2、理论上能减少GC暂停时间。 
3、可以在进程间共享,减少JVM间的对象复制,使得JVM的分割部署更容易实现。 。因为堆内在flush到远程时,会先复制到直接内存(非堆内存),然后在发送;而堆外内存相当于省略掉了这个工作。

4、它的持久化存储可以支持快速重启,同时还能够在测试环境中重现生产数据。

堆外内存缺点:

而福之祸所依,自然也有不好的一面: 

  1 堆外内存难以控制,如果内存泄漏,那么很难排查 
  2 堆外内存相对来说,不适合存储很复杂的对象。一般简单的对象或者扁平化的比较适合。

JVM可以使用的内存分外2种:堆内存和堆外内存.

    堆内存完全由JVM负责分配和释放,如果程序没有缺陷代码导致内存泄露,那么就不会遇到java.lang.OutOfMemoryError这个错误。

    使用堆外内存,就是为了能直接分配和释放内存,提高效率。JDK5.0之后,代码中能直接操作本地内存的方式有2种:使用未公开的Unsafe和NIO包下ByteBuffer。

    关于Unsafe对象的简介和获取方式,(不安全,不建议使用)

    使用ByteBuffer分配本地内存则非常简单,直接ByteBuffer.allocateDirect(10 * 1024 * 1024)即可。

    C语言的内存分配和释放函数malloc/free,必须要一一对应,否则就会出现内存泄露或者是野指针的非法访问。java中我们需要手动释放获取的堆外内存吗?

    我们一起来看看NIO中提供的ByteBuffer

 我们将最大堆外内存设置成40M,运行这段代码会发现:程序可以一直运行下去,不会报OutOfMemoryError。如果使用了-verbose:gc -XX:+PrintGCDetails,会发现程序频繁的进行垃圾回收活动。那么DirectByteBuffer究竟是如何释放堆外内存的?

    我们修改下JVM的启动参数,重新运行之前的代码:

    

 与之前的JVM启动参数相比,增加了-XX:+DisableExplicitGC,这个参数作用是禁止代码中显示调用GC。代码如何显示调用GC呢,通过System.gc()函数调用。如果加上了这个JVM启动参数,那么代码中调用System.gc()没有任何效果,相当于是没有这行代码一样。

显然堆内存(包括新生代和老年代)内存很充足,但是堆外内存溢出了。也就是说NIO直接内存的回收,需要依赖于System.gc()。如果我们的应用中使用了java nio中的direct memory,那么使用-XX:+DisableExplicitGC一定要小心,存在潜在的内存泄露风险。

     我们知道java代码无法强制JVM何时进行垃圾回收,也就是说垃圾回收这个动作的触发,完全由JVM自己控制,它会挑选合适的时机回收堆内存中的无用java对象。

代码中显示调用System.gc(),只是建议JVM进行垃圾回收,但是到底会不会执行垃圾回收是不确定的,可能会进行垃圾回收,也可能不会。什么时候才是合适的时机呢?一般来说是,系统比较空闲的时候(比如JVM中活动的线程很少的时候),还有就是内存不足,不得不进行垃圾回收。

我们例子中的根本矛盾在于:堆内存由JVM自己管理,堆外内存必须要由我们自己释放;堆内存的消耗速度远远小于堆外内存的消耗,但要命的是必须先释放堆内存中的对象,才能释放堆外内存,但是我们又不能强制JVM释放堆内存。

 

Direct Memory的回收机制:Direct Memory是受GC控制的,例如ByteBuffer bb = ByteBuffer.allocateDirect(1024),这段代码的执行会在堆外占用1k的内存,Java堆内只会占用一个bb对象的指针引用的大小,堆外的这1k的空间只有当bb对象被回收时,才会被回收,这里会发现一个明显的不对称现象,就是堆外可能占用了很多,而堆内没占用多少,导致还没触发GC,那就很容易出现Direct Memory造成物理内存耗光。

Direct ByteBuffer分配出去的内存其实也是由GC负责回收的,而不像Unsafe是完全自行管理的,Hotspot在GC时会扫描Direct ByteBuffer对象是否有引用,如没有则同时也会回收其占用的堆外内存。

 

转载地址:https://blog.csdn.net/liao0801_123/article/details/85077666 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!

上一篇:工具类or静态类使用Spring管理的service(dao,mapper),构造初始化
下一篇:Unsafe类详解(用来分配堆外内存)

发表评论

最新留言

不错!
[***.144.177.141]2024年03月18日 17时08分41秒

关于作者

    喝酒易醉,品茶养心,人生如梦,品茶悟道,何以解忧?唯有杜康!
-- 愿君每日到此一游!

推荐文章

html描述列表在线实例,HTML的列表标签 2019-04-21
mysql 允许网络连接_MySQL 权限问题 允许所有网络的连接 2019-04-21
mysql查询服务器_MySQL查询执行 2019-04-21
oracle和mysql空字符串_Oracle中Null与空字符串' '的区别 2019-04-21
android webview静态方法,在android webview中加载静态页面 2019-04-21
Android网络期末考,Android 面试之「网络基础篇」 2019-04-21
html如何修改按钮样式,css怎么设置按钮样式? 2019-04-21
nextcloud如何填写数据库_OMV利用Docker配置nextcloud,实现个人网盘的搭建! 2019-04-21
点击定位到指定位置_使用Word书签功能轻松定位到指定位置或某个特定内容 2019-04-21
图像控制点 形变_几何校正中控制点的选取.doc 2019-04-21
cpu超线程优缺点_今天看了下百度百科!看到了超线程的优缺点啊!转! 2019-04-21
python2和3安装后怎样切换 mac_Mac下安装配置Python2和Python3并相互切换使用 2019-04-21
python错误代码40035_python-(matplotlib.pyplot)散点图轴的顺序错误 2019-04-21
yaf mysql_Yaf框架的配置 2019-04-21
卸载linux下的mysql数据库实例_Linux下卸载MySQL数据库实例教程 2019-04-21
mysql导到相对位置_MySQL数据备份与恢复 2019-04-21
mysql安装连接测试c_Ubuntu 12.04下安装mysql并用C程序测试连接 2019-04-21
mysql数据库与mysqli_通过 PHP Mysqli 扩展与 MySQL 数据库交互 2019-04-21
java web权限_Javaweb权限管理设计思路 2019-04-21
fastdfs java上传文件_FastDFS java客户端文件上传demo 2019-04-21