java中的缓冲类HeapByteBuffer和DirectByteBuffer的区别

news2025/1/20 12:14:35

使用之前写的文章里的例子

https://blog.csdn.net/zlpzlpzyd/article/details/135292683

HeapByteBuffer

import java.io.File;
import java.io.FileInputStream;
import java.io.Serializable;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
 
public class TestHeapByteBuffer implements Serializable {
 
    public static void main(String[] args) {
        long start = System.currentTimeMillis();
        File file = new File(Control.PATH);
        ByteBuffer byteBuffer = ByteBuffer.allocate(Control.SIZE);
        try (FileChannel fileChannel = new FileInputStream(file).getChannel()) {
            while (fileChannel.read(byteBuffer) > 0) {
                byteBuffer.clear();
            }
        } catch (Throwable e) {
            e.printStackTrace();
        }
        long duration = System.currentTimeMillis() - start;
        System.out.println(duration);
    }
}

HeapByteBuffer 在堆上创建的缓冲区,通过 FileChannel 的 read() 读取缓冲区时,会先通过 IOUtil.read() 将 ByteBuffer 获取一个临时 DirectByteBuffer 添加到原来的 ByteBuffer 中。

间接调用 Util 的 getTemporaryDirectBuffer() 获取临时的 DirectByteBuffer,使用完毕后销毁。

可见,HeapByteBuffer 使用的缓冲区不是单纯在堆上处理,还需要借助于 DirectByteBuffer 来处理。

这样就面临一个问题,每次调用 read() 都会造成一个开销问题。

上面只是拿了 read() 来讲解,write() 类似。

DirectByteBuffer

import java.io.File;
import java.io.FileInputStream;
import java.io.Serializable;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
 
public class TestDirectByteBuffer implements Serializable {
 
    public static void main(String[] args) {
        long start = System.currentTimeMillis();
        File file = new File(Control.PATH);
        ByteBuffer byteBuffer = ByteBuffer.allocateDirect(Control.SIZE);
        try (FileChannel fileChannel = new FileInputStream(file).getChannel()) {
            while (fileChannel.read(byteBuffer) > 0) {
                byteBuffer.clear();
            }
        } catch (Throwable e) {
            e.printStackTrace();
        }
        long duration = System.currentTimeMillis() - start;
        System.out.println(duration);
    }
}

直接在堆外分配的内存缓冲区,在构造器中有三个操作,如下图

向 Bits 中的变量设置默认值

获取 DirectByteBuffer 的最大直接内存

在 VM 类中可以看到,默认最大值是 64MB,可以通过参数 -XX:MaxDirectMemorySize 进行修改,具体修改参数修改可以参见如下的官方文档

https://docs.oracle.com/javase/8/docs/technotes/tools/unix/java.html

接下来是给 Bits 中的变量赋值

因为参数 size 和 cap 是创建 ByteBuffer 时指定的,totalCapacity 默认值为 0,加上 maxMemory 的值为 64 MB,所以比较式右边的值为 64 MB,cap <= 右边的数据,所以进入循环处理,第一次就停止循环到被调用方停止当前执行方法。

通过 Unsafe 进行分配内存

调用 Unsafe 的 allocateMemory() 先向内存中分配一个新块

调用 Unsafe 的 setMemory() 向分配的内存块中设置对应的字节

创建内存清理者

其中创建的 Deallocator 是一个内部类,创建的对象作为一个后台线程处理。Cleaner 是一个 PhantomReference 对象,即对应的值无法获取。

线程执行的时候会触发 Unsafe 的 freeMemory() 和 Bits.unreserveMemory() 触发堆外的内存清理操作,但是触发时间无法控制。

总结

通过分析可以得出如下

HeapByteBuffer 使用简单,每次执行数据读取写入间接创建 DirectByteBuffer,效率低。

DirectByteBuffer 使用相对麻烦,但是效率高,需要考虑到通过 ByteBuffer 分配的缓冲区与 jvm 参数 -XX:MaxDirectMemorySize 是否合理的问题,不然的在运行过程中会出现内存溢出问题。内部是通过 PhantomReference(Cleaner 的父类)来处理创建的缓冲区,最终通过 Reference 的 clean() 来间接执行堆外内存回收。

为什么使用 DirectByteBuffer

摘自网络上的解答

https://cheng-dp.github.io/2018/12/11/direct-memory-and-direct-byte-buffer/

减少复制操作,加快传输速度

HotSpot虚拟机中,GC除了CMS算法之外,都需要移动对象。

在NIO实现中(如: FileChannel.read(ByteBuffer dst), FileChannel.write(ByteByffer src)), 底层要求连续的内存,且使用期间不得变动, 如果提供的Buffer是HeapByteBuffer,为了保证在数据传输时,被传输的byte数组背后的对象不会被GC回收或者移动,JVM会首先将堆中的byte数组拷贝至直接内存,再由直接内存进行传输。

那么,相比于HeapByteBuffer在堆上分配空间,直接只用DirectByteBuffer在直接内存分配就节省了一次拷贝,加快了数据传输的速度。

减少GC压力

虽然GC仍然管理DirectByteBuffer,但基于DirectByteBuffer分配的空间不属于GC管理,如果IO数量较大,可以明显降低GC压力。

http://lovestblog.cn/blog/2015/05/12/direct-buffer/

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

DirectByteBuffer 的使用场景

需要频繁操作的小内存,并且仅仅是临时存在一会的,都建议使用堆外内存,并且做成缓冲池,不断循环利用这块内存。

DirectByteBuffer 使用注意事项

摘自网络上的解答

创建和销毁比普通Buffer慢。

虽然DirectByteBuffer的传输速度很快,但是创建和销毁比普通Buffer慢。因此DirectByteBuffer适合只是短时使用需要频繁创建和销毁的场合。

使用直接内存要设置-XX:MaxDirectMemorySize指定最大大小。

直接内存不受GC管理,而基于DirectByteBuffer对象的自动回收过程并不稳定,如DirectByteBuffer对象被MinorGC经过MinorGC进入老年代,但是由于堆内存充足,迟迟没有触发Full GC,DirectByteBuffer将不会被回收,其申请的直接内存也就不会被释放,最终造成直接内存的OutOfMemoryError。

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

参考链接

https://www.zhihu.com/question/60892134

https://www.cnblogs.com/Chary/p/16718014.html

https://developer.aliyun.com/article/763697

https://juejin.cn/post/6844903744119783431

​​​​​​https://www.infoq.cn/news/2014/12/external-memory-heap-memory/

https://blog.csdn.net/flyzing/article/details/115388720

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1344689.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Tomcat与Servlet是什么关系

Tomcat与Servlet是什么关系 Apache Tomcat和Servlet之间存在密切的关系&#xff0c;可以说它们是一对密切合作的组件。下面是它们的关系&#xff1a; Tomcat是Servlet容器&#xff1a; Tomcat是一个开源的、轻量级的Servlet容器。Servlet容器是一个Web服务器扩展&#xff0c;用…

经典文献阅读之--OccNeRF(基于神经辐射场的自监督多相机占用预测)

0. 简介 作为基于视觉感知的基本任务&#xff0c;3D占据预测重建了周围环境的3D结构。它为自动驾驶规划和导航提供了详细信息。然而&#xff0c;大多数现有方法严重依赖于激光雷达点云来生成占据地面真实性&#xff0c;而这在基于视觉的系统中是不可用的。之前我们介绍了《经典…

php获取访客IP、UA、操作系统、浏览器等信息

最近有个需求就是获取下本地的ip地址、网上搜索了相关的教程&#xff0c;总结一下分享给大家、有需要的小伙伴可以参考一下 一、简单的获取 User Agent 信息代码: echo $_SERVER[HTTP_USER_AGENT]; 二、获取访客操作系统信息: /** * 获取客户端操作系统信息,包括win10 * pa…

一语道破爬虫,来揭开爬虫面纱

目录 一、爬虫&#xff08;网络蜘蛛(Spider)&#xff09; 1.1、是什么&#xff1a; 1.2、学习的原因 1.3、用在地方&#xff1a; 1.4、是否合法&#xff1a; 1.5、后果 案例&#xff1a; 二、应用领域 三、Robots协议 四、抓包 4.1、浏览器抓包 4.2、抓包工具 常见…

DDAE: Denoising Diffusion Autoencoders are Unified Self-supervised Learners

DDAE: Denoising Diffusion Autoencoders are Unified Self-supervised Learners Paper&#xff1a;https://arxiv.org/abs/2303.09769 Code&#xff1a;https://github.com/FutureXiang/ddae TL; DR&#xff1a;扩散模型的训练其实就是训练一个去噪模型&#xff0c;考虑到类似…

竞赛保研 基于机器视觉的12306验证码识别

文章目录 0 简介1 数据收集2 识别过程3 网络构建4 数据读取5 模型训练6 加入Dropout层7 数据增强8 迁移学习9 结果9 最后 0 简介 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 基于机器视觉的12306验证码识别 该项目较为新颖&#xff0c;适合作为竞赛课题方向…

Harmony全局应用生命周期 EntryAbility.ts 讲解

之前 我们说过 page页面的生命周期 组件的生命周期 其实他和uni一样有一个整个应用的生命周期 我们如下图打开EntryAbility.ts 这是我们整个程序app的状态控制 他这里也有几个全局的生命周期 比如 我们手机 点开当前 App 启动 app 会触发 它的 onCreate 生命周期 当我们从手…

【并发设计模式】聊聊等待唤醒机制的规范实现

在多线程编程中&#xff0c;其实就是分工、协作、互斥。在很多场景中&#xff0c;比如A执行的过程中需要同步等待另外一个线程处理的结果&#xff0c;这种方式下&#xff0c;就是一种等待唤醒的机制。本篇我们来讲述等待唤醒机制的三种实现&#xff0c;以及对应的应用场景。 G…

Linux:apache优化(5)—— 隐藏版本号

防盗链&#xff1a;就是防止别人盗取你们公司的图片、文件、视频 作用&#xff1a;防盗链就是防止别人盗用服务器中的图片、文件、视频等相关资源。运维人员可以通过apache提供rewrite模块进行优化。 配置项&#xff1a; RewriteEngine ON ##打开网页重写功能 RewriteCond …

微服务全链路灰度方案介绍

目录 一、单体架构下的服务发布 1.1 蓝绿发布 二、微服务架构下的服务发布 三、微服务场景下服务发布的问题 四、全链路灰度解决方案 4.1 物理环境隔离 4.2 逻辑环境隔离 4.3 全链路灰度方案实现技术 4.3.1 标签路由 4.3.2 节点打标 4.3.3 流量染色 4.3.4 分布式链路…

基于ssm的程序设计实践项目管理系统+jsp论文

摘 要 现代经济快节奏发展以及不断完善升级的信息化技术&#xff0c;让传统数据信息的管理升级为软件存储&#xff0c;归纳&#xff0c;集中处理数据信息的管理方式。本实践项目管理系统就是在这样的大环境下诞生&#xff0c;其可以帮助管理者在短时间内处理完毕庞大的数据信息…

2023跨境电商年度总结:好用的跨境电商工具集合

不知不觉2023年已到了尾声&#xff0c;迎来崭新的2024年。经过年末旺季冲击的跨境电商&#xff0c;也来到了总结阶段。常言道工具选的好&#xff0c;工作轻松倍。在我们的跨境工作中少不得那些高效有用的工具加持。 接下来小编为大家盘点2023年度跨境工具网站&#xff0c;快来…

shell编程一

shell 定义 Shell 也是一种程序设计语言&#xff0c;它有变量&#xff0c;关键字&#xff0c;各种控制语句&#xff0c;有自己的语法结构&#xff0c;利用shell程序设计语 可以编写功能强、代码简短的程序 #! Shebang 定义解释器 shell的分类和切换 # cat /etc/shells /bin/sh…

AI大模型:未来科技的新篇章

目录 1AI大模型&#xff1a;未来科技的新篇章 2AI超越数学家攻克经典数学难题&#xff1b;非侵入式设备解码大脑思维 1AI大模型&#xff1a;未来科技的新篇章 随着科技的飞速发展&#xff0c;人工智能&#xff08;AI&#xff09;已经成为了我们生活中不可或缺的一部分。而AI大…

Ubuntu22.04 安装教程

系统下载 Ubuntu官网下载 清华源镜像 安装流程 1. 选择安装语言 2. 选择是否在安装时更新 为了系统安装速度一般选择安装时不更新&#xff0c;安装后自行更新 3. 选择系统语言和键盘布局 4. 选择安装模式 5. 配置网络信息 6. 设置静态IP 7. 配置代理信息 8. 配置Ubuntu镜像…

(windows2012共享文件夹和防火墙设置

windows2012共享文件夹和防火墙设置 1.windows2012文件夹共享1.共享和高级共享的区别![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/0d815cc6862a4c7a99be11442fb5d950.png#pic_center) 2.windows的防火墙设置1.防火墙设置8080端口让tomot可以在主机可以访问1.新建…

iptables 防火墙(二)

目录 1. SNAT 策略及应用 1.1 SNAT策略概述 1. 只开启路由转发&#xff0c;未设置地址转换的情况 2. 开启路由转发&#xff0c;并设置SNAT转换的情况 1.2 SNAT策略的应用 1. 2.1 共享固定IP上网 &#xff08;1&#xff09;打开网关的路由转发 &#xff08;2&#xff09;…

ArkUI自定义组件的使用

文章概叙 本文主要讲的是如何在ArkUi红创建一个自定义的组件&#xff0c;并且在新的页面中引用&#xff0c;其实依旧是自定义函数&#xff0c;只是既可以在外部使用&#xff0c;也可以作为组件引入. 介绍 前面的文章中&#xff0c;已经对tab页面的内容编辑的差不多了&#x…

C语言经典算法【每日一练】20

题目&#xff1a;有一个已经排好序的数组。现输入一个数&#xff0c;要求按原来的规律将它插入数组中。 1、先排序 2、插入 #include <stdio.h>// 主函数 void main() {int i,j,p,q,s,n,a[11]{127,3,6,28,54,68,87,105,162,18};//排序&#xff08;选择排序&#xff09…