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 千行代码实现的迷你文本编辑器。该项…

STL容器——map

由于某些原因其实就是作者咕咕咕了 &#xff0c;这篇文章到2023年的最后一天才更出来 map的认识 关于map&#xff0c;这是一种常用的工具 基本上可以看成一个下标可以为任意的数组 注意&#xff0c;这里的任意&#xff0c;包括C中所有类型的所有取值可能 也就是说下标是什…

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

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

c++对c的加强

目录 提出了命名空间的概念 实用性增强 register关键字增强 变量检测增强 struct类型加强 C中所有的变量和函数都必须有类型 新增bool数据类型 提出了命名空间的概念 命名空间将全局作用域分成不同的部分 不同命名空间中的标识符可以同名而不会发生冲突 命名空间可以相互…

TypeScript 类方法装饰器

type ClassMethodDecorator (value: Function,context: {kind: method;name: string | symbol;static: boolean;private: boolean;access: { get: () > unknown };addInitializer(initializer: () > void): void;} ) > Function | void; 1、如果装饰器返回一个函数就…

C# 中 async/await 遇上 forEach 两种写法,是否按照遍历?

在 C# 中&#xff0c;async/await 与 forEach 可以搭配使用&#xff0c;但需要注意的是&#xff0c;forEach 本身不是一个异步操作&#xff0c;它会按顺序同步地遍历集合中的元素&#xff0c;并将每个元素作为参数传递给回调函数。因此&#xff0c;如果在 forEach 循环中使用 a…

【嵌入式开发 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锁定代码…

CAN,SPI,IIC,USART每帧的组成

字节是计算机中用于存储数据的基本单位&#xff0c;一个字节由8个二进制位组成。在计算机科学中&#xff0c;字节的大小是固定的&#xff0c;即1字节8位。1比特1位 在不同的数据类型中&#xff0c;字节的大小也不同。例如&#xff0c;在ASCII码中&#xff0c;一个英文字母或数…

【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],…

Java 语法糖的介绍

在Java编程中&#xff0c;语法糖是一种简化代码的技巧&#xff0c;它可以使代码更易读、易写&#xff0c;同时提高开发效率。尽管从语法上看&#xff0c;它更像是一种装饰&#xff0c;但它能给我们的代码带来革命性的改变。 一、什么是Java语法糖&#xff1f; "语法糖&q…

删除重复字符

本题要求编写程序&#xff0c;将给定字符串去掉重复的字符后&#xff0c;按照字符ASCII码顺序从小到大排序后输出。 输入格式&#xff1a; 输入是一个以回车结束的非空字符串&#xff08;少于80个字符&#xff09;。 输出格式&#xff1a; 输出去重排序后的结果字符串。 输…

leetcode每日一题42

107.二叉树的层序遍历II 就层序遍历后reverse一下 class Solution { public:vector<vector<int>> levelOrderBottom(TreeNode* root) {queue<TreeNode*> que;if(root!nullptr)que.push(root);vector<vector<int>> result;while(!que.empty()){…

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

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