java零拷贝zero copy MappedByteBuffer

目录

调用操作系统的 mmap

未使用 mmap 的文件通过网络传输的过程

使用 mmap 的文件通过网络传输的过程

使用例子

调用操作系统的 sendfile()

在 java 中的具体实现

mmap的优劣

mmap 的不足

mmap 的优点

mmap 的使用场景


对于零拷贝(zero copy),目前操作系统支持多种方式,具体如下

调用操作系统的 mmap

在之前的页缓存文章的基础上

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

如果在 linux 上如果直接对 page cache 怎么办?

鉴于 java 语言是建立在 jvm 基础上调用操作系统的 api 来对机器资源进行访问的,可以通过 mmap 来实现,从 java 1.4 开始提供了 FileChannel 的 map() 来实现这个功能,这样就可以类似指针的方式来直接操作文件了。这样带来的好处是,不用进行用户态和内核态的切换了,减少了机器的资源开销。

未使用 mmap 的文件通过网络传输的过程

可见发生了4次用户态与内核态的上下文切换(调用 read()后返回数据与调用write()后返回数据),4次数据拷贝(两次DMA拷贝,两次CPU拷贝)。
传统的IO性能是非常差的,所以,要想提高文件传输的性能,就需要减少用户态与内核态的上下文切换和内存拷贝的次数。

使用 mmap 的文件通过网络传输的过程

需要 4 次上下文切换,因为系统调用还是 2 次。但是拷贝从4次变成了3次。

使用例子

java 中通过 MappedByteBuffer 来实现直接对 page cache 的操作。

import java.io.IOException;
import java.io.Serializable;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;public class TestMapMemeryBuffer2 implements Serializable {private final static String CONTENT = "Zero copy implemented by MappedByteBuffer";private final static String FILE_NAME = "mmap.txt";public static void main(String[] args) throws IOException {xieshuju();dushuju();}public static void xieshuju() throws IOException {/**写文件数据* 打开文件通道 fileChannel 并提供读权限、写权限和数据清空权限,通过 fileChannel 映射到一个可写的内存缓冲区 mappedByteBuffer,* 将目标数据写入 mappedByteBuffer,通过 force() 方法把缓冲区更改的内容强制写入本地文件。*/Path path = Paths.get("E:/data",FILE_NAME);if (Files.notExists(path)) {Files.createDirectories(path.getParent());Files.createFile(path);}byte[] bytes = CONTENT.getBytes(StandardCharsets.UTF_8);try (FileChannel fileChannel = FileChannel.open(path, StandardOpenOption.READ,StandardOpenOption.WRITE, StandardOpenOption.TRUNCATE_EXISTING)) {MappedByteBuffer mappedByteBuffer = fileChannel.map(FileChannel.MapMode.READ_WRITE, 0, bytes.length);if (mappedByteBuffer != null) {mappedByteBuffer.put(bytes);mappedByteBuffer.force();}} catch (Throwable e) {e.printStackTrace();}}public static void dushuju() throws IOException {/*** 读文件数据:打开文件通道 fileChannel 并提供只读权限,通过 fileChannel 映射到一个只可读的内存缓冲区 mappedByteBuffer,* 读取 mappedByteBuffer 中的字节数组即可得到文件数据。* */Path path = Paths.get("E:/data",FILE_NAME);if (Files.notExists(path)) {Files.createDirectories(path.getParent());Files.createFile(path);}int length = CONTENT.getBytes(StandardCharsets.UTF_8).length;try (FileChannel fileChannel = FileChannel.open(path, StandardOpenOption.READ)) {MappedByteBuffer mappedByteBuffer = fileChannel.map(FileChannel.MapMode.READ_ONLY, 0, length);if (mappedByteBuffer != null) {byte[] bytes = new byte[length];mappedByteBuffer.get(bytes);String content = new String(bytes, StandardCharsets.UTF_8);System.out.println(content);}} catch (Throwable e) {e.printStackTrace();}}
}

调用操作系统的 sendfile()

在 Linux 内核版本 2.1 中,提供了1个专门发送文件的系统调用函数 sendfile()。

它可以替代前面的 read() 和 write() 这两个系统调用,这样就可以减少1次系统调⽤,文件传输变为在操作系统执行,不需要应用程序的参与,也就减少了 2 次上下文切换的开销。

这样就只有 2 次上下文切换,和 3 次数据拷贝。

在 java 中的具体实现

FileChannel 的 transferFrom() 和 transferTo()。

两者使用调用方式不同,transferFrom() 是目标 FileChannel 调用,transferTo() 是源 FileChannel 调用。

https://blog.csdn.net/weixin_44371237/article/details/122322890

以上的还不是真正的零拷贝技术,如果网卡支持 SG-DMA(The Scatter-Gather DirectMemory Access)技术(和普通的 DMA 有所不同),可以减少通过 CPU 把内核缓冲区的数据拷贝到 socket 缓冲区的过程。

Linux运行这条命令查看网卡是否支持 SG-DMA

$ ethtool -k eth0 | grep scatter-gather
scatter-gather: on

这样,最终只有2次上下文切换和2 次数据拷贝。

mmap的优劣

mmap 的不足

鉴于操作系统的 page cache 为 4KB,所以在使用过程中最好是 4KB 的整数倍,不然会造成内存空间浪费。

map方法中 size 的类型是 long,但是在注释中指定不能大于 Integer#MAX_VALUE,也就是2147483647字节,换算一下大概是2G。也就是说 MappedByteBuffer 的最大值是2G,一次最多只能 map 2G 的数据。

由于 MappedByteBuffer 最终的实现类为 DirectByteBuffer,即使用了堆外内存,这就使得在使用的时候对内存回收时变得困难。
如果针对大文件使用 MappedByteBuffer,会造成内存不足的情况,其他一些经常使用的文件会造成经常被回收的情况(因为 page cache 的在操作系统实现了 lru 算法来处理)。

mmap内存映射的大小始终是整数页,因此对于文件实际大小和映射的空间之间多少会有差异,这个差异的空间是被浪费的,对于小文件来说这个浪费比例被放大,因此 mmap 更适合频繁操作的大文件。频繁映射大量不同大小的内存,会导致内存碎片化。

针对大文件的传输,不应该使用 Page Cache,也就是说不应该使用零拷贝技术,因为可能由于 Page Cache 被大文件占据,由于大文件难以命中 Page Cache 缓存,导致热点小文件无法命中 Page Cache,这样在高并发的环境下,会带来严重的性能问题。

传输大文件的时候,使用异步 IO + 直接 IO,因为可以绕过 Page Cache
传输小文件的时候,使用零拷贝技术
 

针对所谓的文件大小的定义,需要通过压力测试来验证一下,这个需要后面看一下。

MappedByteBuffer 是没有close方法的,即使它的 FileChannel 被close了,MappedByteBuffer 仍然处于打开状态,只有JVM进行垃圾回收的时候才会被关闭。而这个时间是不确定的。

对于具体的写入磁盘时间是由操作系统来决定的,如果想要马上写入磁盘需要手动调用 force()。

mmap 的优点

mmap基于操作系统的 mmap 的内存映射技术,通过 MMU 映射文件,将文件直接映射到用户态的内存地址,使得对文件的操作不再是 write/read,而转化为直接对内存地址的操作,使随机读写文件和读写内存相似的速度。

把文件映射到用户空间里的虚拟内存,省去了从内核缓冲区复制到用户空间的过程,文件中的位置在虚拟内存中有了对应的地址,可以像操作内存一样操作这个文件,这样的文件读写文件方式少了数据从内核缓存到用户空间的拷贝,效率很高。

将用户态和内核态的重操作减少了。

mmap 的使用场景

频繁操作的文件,因为是基于 page cache 实现的,主要将磁盘的文件暂时缓存到内存中。如果只是用一次或者次数很少,放在内存里没有必要。

参考链接

https://blog.csdn.net/alex_xfboy/article/details/90174840

https://blog.csdn.net/bookssea/article/details/122099186

https://blog.csdn.net/qq_45038038/article/details/134975039

https://blog.csdn.net/qq_39668099/article/details/130240286

https://juejin.cn/post/6921977140946845704

https://blog.csdn.net/m0_50662680/article/details/128420713

https://www.cnblogs.com/flydean/p/io-nio-mappedbytebuffer.html

https://blog.csdn.net/yzh_1346983557/article/details/119760911

https://www.cnblogs.com/sky-heaven/p/16280797.html

https://mp.weixin.qq.com/s/oPv1-wrhYjiOC1o0M0tjMA

https://www.cnblogs.com/jmcui/p/15256464.html

https://www.cnblogs.com/liujinhui/p/15847633.html

https://zhuanlan.zhihu.com/p/377237946

https://blog.csdn.net/andybegin/article/details/129304899

https://blog.csdn.net/dyuan134/article/details/130126955

https://zhuanlan.zhihu.com/p/54762255

https://tech.meituan.com/2017/05/19/about-desk-io.html

https://www.jianshu.com/p/59dad2d290a1

https://www.jianshu.com/p/c83fa8bd564f

https://blog.csdn.net/NF_ALONG/article/details/129399559

https://zhuanlan.zhihu.com/p/439380628

https://blog.csdn.net/xystrive/article/details/125692926

https://zhuanlan.zhihu.com/p/607416958

https://zhuanlan.zhihu.com/p/665075935

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

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

相关文章

C语言实验4:指针

目录 一、实验要求 二、实验原理 1. 指针的基本概念 1.1 指针的定义 1.2 取地址运算符(&) 1.3 间接引用运算符(*) 2. 指针的基本操作 2.1 指针的赋值 2.2 空指针 3. 指针和数组 3.1 数组和指针的关系 3.2 指针和数…

【Linux】内核编译 镜像制作

文章目录 一、Ubuntu内核编译1.1 为什么自己编译内核1.2 Ubuntu 内核源码下载1.21 内核的作用1.22 Linux内核与ubuntu内核1.23 Ubuntu内核源码获取 1.3 在Windows系统下编译ubuntu内核1.4 在Linux系统下编译ubuntu内核 二、镜像制作 一、Ubuntu内核编译 1.1 为什么自己编译内核…

用LCD循环右移显示“Welcome to China“

#include<reg51.h> //包含单片机寄存器的头文件 #include<intrins.h> //包含_nop_()函数定义的头文件 sbit RSP2^0; //寄存器选择位&#xff0c;将RS位定义为P2.0引脚 sbit RWP2^1; //读写选择位&#xff0c;将RW位定义为P2.1引脚 sbit EP2^2; //使能…

Debezium日常分享系列之:向 Debezium 连接器发送信号

Debezium日常分享系列之&#xff1a;向 Debezium 连接器发送信号 一、概述二、激活源信号通道三、信令数据集合的结构四、创建信令数据集合五、激活kafka信号通道六、数据格式七、激活JMX信号通道八、自定义信令通道九、Debezium 核心模块依赖项十、部署自定义信令通道十一、信…

【C# 技术】 C# 常用排序方式——自定义数据排序

C# 常用排序方式——自定义数据排序 前言 在最近的项目中经常会对C#中的数据进行排序&#xff0c;对于基本数据类型&#xff0c;其排序方式比较简单&#xff0c;只需要调用内置算法即可实现&#xff0c;但对于自定义数据类型以及自定义排序规则的情况实现起来就比较麻烦&…

区分LR(0),SLR(1),LR(1)和LALR(1)

目录 对于LR(0)文法&#xff1a; 对于SLR(1)文法&#xff1a; 对于LR(0)和SLR(1)文法&#xff1a; 对于LR(1)和SLR(1)文法&#xff1a; 对于LALR(1)文法&#xff1a; 例题1&#xff1a; 例题2&#xff1a; 例题3&#xff1a; 例题4&#xff1a; 这几个文法大致的步骤都…

推荐几个贼有意思的开源项目!

这里有实战项目、入门教程、黑科技、开源书籍、大厂开源项目等&#xff0c;涵盖多种编程语言 Python、Java、Go、C/C、Swift...让你在短时间内感受到开源的魅力&#xff0c;对编程产生兴趣&#xff01; C 项目 1、kilo&#xff1a;不到 1 千行代码实现的迷你文本编辑器。该项…

Portraiture4.1汉化版PS磨皮插件(支持原生m1芯片m2)

Portraiture汉化版PS磨皮插件。本期推荐一款全新ai算法ps2024中文汉化版ps磨皮插件Portraiture 4.1.2美颜滤镜安装包最新版ps调整肤色插件! 全新Portraiture 4.1.2版本PS人像修图美颜磨皮插件&#xff0c;升级AI算法&#xff0c;并支持多人及全身磨皮美化模式&#xff0c;推荐…

【嵌入式开发 Linux 常用命令系列 7.3 -- linux 命令行数值计算】

文章目录 linux 命令行数值计算使用 awk使用 bc 命令使用 Bash 的内置算术扩展使用 expr脚本命令实现 linux 命令行数值计算 在 Linux 命令行中&#xff0c;您可以使用多种方法来执行基本的数学运算。以下是一些示例&#xff1a; 使用 awk awk 是一个强大的文本处理工具&…

【C#】知识点实践序列之Lock的输出多线程信息

大家好&#xff0c;我是全栈小5&#xff0c;欢迎来到《小5讲堂之知识点实践序列》文章。 2023年第2篇文章&#xff0c;此篇文章是C#知识点实践序列之Lock知识点&#xff0c;博主能力有限&#xff0c;理解水平有限&#xff0c;若有不对之处望指正&#xff01; 本篇在Lock锁定代码…

【python_数据分组】

对excel按照标签进行分组&#xff0c;例如按照“开票主体和对方公司”进行分组&#xff0c;并获取对应的明细。 表格如下&#xff1a; def main(excel_data):result {}for d in excel_data:if str(d[0])str(d[1]) in result:result[str(d[0])str(d[1])].append([d[0],d[1],…

web component - 使用HTML Templates和Shadow DOM构建现代UI组件

Web Component是一种用于构建可重用的UI组件的技术。它使用标准化的浏览器API&#xff0c;包括Custom Elements、Shadow DOM和HTML Templates来实现组件化开发方式。这些API都是现代浏览器原生支持的&#xff0c;因此不需要引入第三方库或框架即可使用。 在这篇博客中&#xf…

第6课 用window API捕获麦克风数据并加入队列备用

今天是2024年1月1日&#xff0c;新年的第一缕阳光已经普照大地&#xff0c;祝愿看到这篇文章的所有程序员或程序爱好者都能在新的一年里持之以恒&#xff0c;事业有成。 今天也是我加入CSDN的第4100天&#xff0c;但回过头看一看&#xff0c;这么长的时间也没有在CSDN写下几篇…

SpringCloud-高级篇(九)

&#xff08;1&#xff09;Seata高可用 我们学习了Seata的各种用法了&#xff0c;Seata的服务是单节点部署的&#xff0c;这个服务如果挂了&#xff0c;整个事务都没有办法完了&#xff0c;下面我们学习Seata的高可用的知识。 实现高可用&#xff0c;还是比较简单&#xff0c;…

QT上位机开发(抽奖软件)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】 用抽奖软件抽奖&#xff0c;是一种很常见的抽奖方式。特别是写这篇文章的时候&#xff0c;正好处于2023年12月31日&#xff0c;也是一年中最后一天…

【华为机试】2023年真题B卷(python)-喊七的次数重排

一、题目 题目描述&#xff1a; 喊7是一个传统的聚会游戏&#xff0c;N个人围成一圈&#xff0c;按顺时针从1到N编号。 编号为1的人从1开始喊数&#xff0c;下一个人喊的数字为上一个人的数字加1&#xff0c;但是当将要喊出来的数字是7的倍数或者数字本身含有7的话&#xff0c;…

新手快速上手掌握基础排序<二>快速排序快速入门

目录 引言 一&#xff1a;快速排序qsort的简介 1.qsort是一个库函数 2.库函数的查询了解方法 3.qsort的具体使用方法 4.qsort函数使用的一些注意点 5.qsort函数的特点 6.代码实现 (1)整数数组的快速排序 &#xff08;2&#xff09;结构体的快速排序&#xff08;学…

使用.Net nanoFramework 驱动ESP32的OLED显示屏

本文介绍如何使用.Net nanoFramework 驱动ESP32的OLED显示屏。我们将会从最基础的部分开始&#xff0c;逐步深入&#xff0c;让你能够理解并实现整个过程。无论你是初学者还是有一定经验的开发者&#xff0c;这篇文章都会对你有所帮助。 1. 硬件准备 1.1 ESP32开发板 这里我们…

PyTorch的Tensor(张量)

一、Tensor概念 什么是张量&#xff1f; 张量是一个多维数组&#xff0c;它是标量、向量、矩阵的高维拓展 Tensor与Variable Variable是torch.autograd中的数据类型&#xff0c;主要用于封装Tensor&#xff0c;进行自动求导。 data: 被包装的Tensorgrad: data的梯度&…

基于OpenCv的车道检测

项目背景和步骤 车道检测是自动驾驶领域不可或缺的一环 具体步骤如下&#xff1a; 一、将图像灰度化&#xff0c;并进行适度的高斯滤波&#xff0c;剔除干扰 二、利用Canny边缘检测&#xff0c;检测出车道和其它物体的边缘 三、使用ROI区域截取&#xff0c;截取需要的部分&a…