深入理解直接内存和零拷贝

news2024/9/23 9:29:46

目录

直接内存深入辨析

堆外内存的优点和缺点

零拷贝

什么是零拷贝?

Linux的I/O机制与DMA

传统数据传送机制

Linux支持的零拷贝

mmap内存映射

sendfile

splice

Java生态圈中的零拷贝

NIO提供的内存映射MappedByteBuffer

NIO提供的sendfile

Kafka中的零拷贝


直接内存深入辨析

        在所有的网络通信和应用程序中,每个 TCP 的 Socket 的内核中都有一个发送缓冲区(SO_SNDBUF)和一个接收缓冲区(SO_RECVBUF),可以使用相关套接字选项来更改该缓冲区大小。

           

       当某个应用进程调用 write 时,内核从该应用进程的缓冲区中复制所有数据到所写套接字的发送缓冲区。如果该套接字的发送缓冲区容不下该应用进程的所有数据(或是应用进程的缓冲区大于套接字的发送缓冲区,或是套接字的发送缓冲区中已有其他数据),假设该套接字是阻塞的,则该应用进程将被投入睡眠。

       内核将不从 write 系统调用返回,直到应用进程缓冲区中的所有数据都复制到套接字发送缓冲区。因此,从写一个 TCP 套接字的 write 调用成功返回仅仅表示我们可以重新使用原来的应用进程缓冲区,并不表明对端的TCP或应用进程已接收到数据。

                           

        Java 程序自然也要遵守上述的规则。但在 Java 中存在着堆、垃圾回收等特性,所以在实际的IO中,在 JVM 内部的存在着这样一种机制:

       在IO读写上,如果是使用堆内存,JDK 会先创建一个 DirectBuffer,再去执行真正的写操作。这是因为,当我们把一个地址通过 JNI 传递给底层的C库的时候,有一个基本的要求,就是这个地址上的内容不能失效。然而,在 GC 管理下的对象是会在 Java 堆中移动的。也就是说,有可能我把一个地址传给底层的 write,但是这段内存却因为GC整理内存而失效了。所以必须要把待发送的数据放到一个 GC 管不着的地方。这就是调用 native 方法之前,数据—定要在堆外内存的原因。

        站在网络通信的角度 DirectBuffer 并没有节省什么内存拷贝,只是Java网络通信里因为HeapBuffer必须多做一次拷贝,使用 DirectBuffer 就会少一次内存拷贝。相比没有使用堆内存的 Java程序,使用直接内存的Java程序当然更快一点。从垃圾回收的角度而言,直接内存不受 GC(新生代的 Minor GC) 影响,只有当执行老年代的Full GC时候才会顺便回收直接内存,整理内存的压力也比数据放到HeapBuffer要小。


堆外内存的优点和缺点

优点:

1. 减少了垃圾回收的工作,因为垃圾回收会暂停其他的工作(可能使用多线程或者时间片的方式,根本感觉不到)

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

缺点:

1. 堆外内存难以控制,如果内存泄漏,那么很难排查。

2. 堆外内存相对来说,不适合存储很复杂的对象。一般简单的对象或者扁平化的比较适合。


零拷贝

什么是零拷贝?

       零拷贝(英语: Zero-copy) 技术是指计算机执行操作时,CPU不需要先将数据从某处内存复制到另一个特定区域。这种技术通常用于通过网络传输文件时节省 CPU 周期和内存带宽。零拷贝技术可以减少数据拷贝和共享总线操作的次数,消除传输数据在存储器之间不必要的中间拷贝次数,从而有效地提高数据传输效率。零拷贝技术减少了用户进程地址空间和内核地址空间之间因为上下文切换而带来的开销可以看出没有说不需要拷贝,只是说减少不必要的拷贝。

Linux的I/O机制与DMA

       早期计算机中,用户进程需要读取磁盘数据,需要 CPU 中断和 CPU 参与,因此效率比较低,发起 IO 请求,每次的 IO 中断,都带来 CPU 的上下文切换。因此出现了——DMA。

       DMA(Direct Memory Access,直接内存存取) 是所有现代电脑的重要特色,它允许不同速度的硬件装置来沟通,而不需要依赖于CPU的大量中断负载。

       DMA 控制器,接管了数据读写请求,减少 CPU 的负担。这样一来,CPU能高效工作了。现代硬盘基本都支持DMA。

传统数据传送机制

        比如:读取文件,再用 socket 发送出去,实际经过四次 copy。伪码实现如下: buffer = File.read() Socket.send(buffer)

第一次:将磁盘文件,读取到操作系统内核缓冲区。

第二次:将内核缓冲区的数据,copy 到应用程序的 buffer。

第三步:将 application 应用程序 buffer 中的数据,copy 到 socket 网络发送缓冲区(属于操作系统内核的缓冲区)。

第四次:将 socket buffer 的数据,copy到网卡,由网卡进行网络传输。

          

       分析上述的过程,虽然引入 DMA 来接管 CPU 的中断请求,但四次 copy 是存在“不必要的拷贝”的。实际上并不需要第二个和第三个数据副本。应用程序除了缓存数据并将其传输回套接字缓冲区之外什么都不做。相反,数据可以直接从读缓冲区传输到套接字缓冲区。第二次和第三次数据 copy其实在这种场景下没有什么帮助反而带来开销,这也正是零拷贝出现的背景和意义。

        同时,read 和 send 都属于系统调用,每次调用都牵涉到两次上下文切换:

                    

       传统的数据传送所消耗的成本:4 次拷贝,4 次上下文切换。4 次拷贝,其中两次是DMA copy,两次是 CPU copy。


Linux支持的零拷贝

mmap内存映射

        硬盘上文件的位置和应用程序缓冲区(application buffers)进行映射(建立一种一一对应关系),由于 mmap()将文件直接映射到用户空间,所以实际文件读取时根据这个映射关系,直接将文件从硬盘拷贝到用户空间,只进行了一次数据拷贝,不再有文件内容从硬盘拷贝到内核空间的一个缓冲区。

        mmap内存映射将会经历:3次拷贝: 1次 cpu copy,2次DMA copy,以及4次上下文切换,调用mmap函数2次,write函数2次。

               

sendfile

       linux 2.1 支持的 sendfile,当调用 sendfile()时,DMA将磁盘数据复制到kernel buffer,然后将内核中的 kernel buffer 直接拷贝到 socket buffer。但是数据并未被真正复制到 socket关联的缓冲区内。取而代之的是,只有记录数据位置和长度的描述符被加入到 socket 缓冲区中。DMA 模块将数据直接从内核缓冲区传递给协议引擎,从而消除了遗留的最后一次复制。但是要注意,这个需要 DMA 硬件设备支持,如果不支持,CPU 就必须介入进行拷贝。

         一旦数据全都拷贝到 socket buffer,sendfile()系统调用将会 return、代表数据转化的完成。socket buffer里的数据就能在网络传输了。sendfile 会经历:3(2,如果硬件设备支持)次拷贝,1(0,,如果硬件设备支持)次 CPU copy, 2 次 DMA copy,以及 2 次上下文切换。

              

splice

       Linux 从 2.6.17 支持splice数据从磁盘读取到OS内核缓冲区后,在内核缓冲区直接可将其转成内核空间其他数据 buffer,而不需要拷贝到用户空间。

       从磁盘读取到内核 buffer 后,在内核空间直接与socket buffer建立pipe管道。和sendfile()不同的是,splice()不需要硬件支持。

        注意splice和sendfile的不同,sendfile是DMA硬件设备不支持的情况下将磁盘数据加载到 kernel buffer后,需要一次CPU copy,拷贝到socket buffer。而splice是更进一步,连这个CPU copy也不需要了,直接将两个内核空间的buffer进行pipe。

         splice会经历2次拷贝: 0 次 cpu copy 2 次 DMA copy,以及2次上下文切换。

           


Java生态圈中的零拷贝

Linux提供的零拷贝技术Java并不是全支持,支持 2 种(内存映射 mmap、sendfile)。

NIO提供的内存映射MappedByteBuffer

        NIO中的FileChannel.map()方法其实就是采用了操作系统中的内存映射方式,底层就是调用 Linux mmap()实现的。将内核缓冲区的内存和用户缓冲区的内存做了一个地址映射。这种方式适合读取大文件, 同时也能对文件内容进行更改,但是如果其后要通过SocketChannel发送,还是需要CPU进行数据的拷贝。

NIO提供的sendfile

         Java NIO 中提供的 FileChannel 拥有 transferTo 和 transferFrom 两个方法,可直接把 FileChannel 中的数据拷贝到另外一个 Channel,或者直接把另外一个 Channel 中的数据拷贝到 FileChannel。该接口常被用于高效的网络/文件的数据传输和大文件拷贝。在操作系统支持的情况下,通过该方法传输数据并不需要将源数据从内核态拷贝到用户态,再从用户态拷贝到目标通道的内核态,同时也避免了两次用户态和内核态间的上下文切换,也即使用了“零拷贝”,所以其性能一般高于Java IO中提供的方法。

Kafka中的零拷贝

        Kafka两个重要过程都使用了零拷贝技术,且都是操作系统层面的狭义零拷贝,一是Producer生产的数据存到broker,二是Consumer从broker读取数据。

        Producer生产的数据持久化到broker,broker里采用mmap文件映射,实现顺序的快速写入。

        Customer从broker读取数据,broker里采用sendfile,将磁盘文件读到 OS 内核缓冲区后,直接转到socket buffer进行网络发送。

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

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

相关文章

npm ERR! code CERT_HAS_EXPIRED

执行npm i报错: npm ERR! code ETIMEDOUT npm ERR! syscall connect npm ERR! errno ETIMEDOUT npm ERR! network request to https://registry.npmjs.org/react-redux failed, reason: connect ETIMEDOUT 104.16.2.35:443 npm ERR! network This is a problem rel…

LangChain 79 LangGraph 从入门到精通一

LangChain系列文章 LangChain 60 深入理解LangChain 表达式语言23 multiple chains链透传参数 LangChain Expression Language (LCEL)LangChain 61 深入理解LangChain 表达式语言24 multiple chains链透传参数 LangChain Expression Language (LCEL)LangChain 62 深入理解Lang…

Python爬虫Scrapyd项目部署详细教程--最完整版本

文章目录 scrapy项目部署1.scrapyd部署工具介绍(1)环境安装 2.scrapy项目部署(1)配置需要部署的项目(2)管理scrapy项目(3)启动项目(4)关闭项目(5&…

【Transformer 】 Hugging Face手册-推理管道 (04/10)

一、说明 这里是Hugging Face手册第四部分,如何使用推理管道;即使您没有特定模式的经验或不熟悉模型背后的底层代码,您仍然可以使用它们通过 pipeline ()进行推理! 二、推理管道 pipeline ()可以轻松使用Hub中的任何模型来推理任…

Go语言的100个错误使用场景(11-20)|项目组织和数据类型

前言 大家好,这里是白泽。 《Go语言的100个错误以及如何避免》 是最近朋友推荐我阅读的书籍,我初步浏览之后,大为惊喜。就像这书中第一章的标题说到的:“Go: Simple to learn but hard to master”,整本书通过分析100…

Channel事件管理类实现(模块三)

目录 类功能 类定义 类实现 类功能 因为涉及到后续一些实现&#xff0c;因此后续可能会进行修改 类定义 class Channel { private:uint32_t _events; // 当前需要监控的事件uint32_t _revents; // 当前连接触发的事件using EventCallback std::function<void()>;E…

自学Java的第58,59天

网络通信 网络通信三要素&#xff1a;ip地址&#xff0c;端口号&#xff0c;协议 ip地址 常用方法 写法 端口号 协议 UDP通信 快速入门 写法&#xff08;客户端&#xff09; &#xff08;服务端&#xff09; UDP通信 多发多收 TCP通信 写法&#xff08;客户端&#xff09; …

Node.js版本管理工具之_Volta

Node.js包管理工具之_Volta 文章目录 Node.js包管理工具之_Volta1. 官网1. 官网介绍2. 特点1. 快( Fast)2. 可靠(Reliable)3. 普遍( Universal) 2. 下载与安装1. 下载2. 安装3. 查看 3. 使用1. 查看已安装的工具包2. 安装指定的node版本3.切换项目中使用的版本 1. 官网 1. 官网…

网络协议梳理

1 引言 在计算机网络中要做到有条不紊地交换数据&#xff0c;就必须遵守一些事先约定好的规则。这些规则明确规定了所交换的数据的格式以及有关的同步问题。这里所说的同步不是狭义的&#xff08;即同频或同频同相&#xff09;而是广义的&#xff0c;即在一定的条件下应当发生什…

大数据本地环境搭建03-Spark搭建

需要提前部署好 Zookeeper/Hadoop/Hive 环境 1 Local模式 1.1 上传压缩包 下载链接 链接&#xff1a;https://pan.baidu.com/s/1rLq39ddxh7np7JKiuRAhDA?pwde20h 提取码&#xff1a;e20h 将spark-3.1.2-bin-hadoop3.2.tar.gz压缩包到node1下的/export/server目录 1.2 解压压…

镜舟科技客户成功团队负责人孟庆欢:湖仓一体将成为数据架构的新范式

大数据产业创新服务媒体 ——聚焦数据 改变商业 随着数字化的概念逐步深入不同领域企业的运营中&#xff0c;业务形态和数字化路径也越来越丰富。这也为企业数据处理、储存的方式提出了更多要求。对于企业&#xff0c;尤其是数据驱动型企业来说&#xff0c;需要强大的解决方案…

LNMP.

一.mysl配置 1.安装mysql yum install mysql-server -y 2.进入mysql配置文件目录 cd /etc/my.cnf.d3.编辑mysql配置文件 vim mysql-server.cnf 在[mysqld]中添加: character-set-serverutf84.启动mysql服务 systemctl start mysqld5.登入mysql mysql 6.创建数据库 cre…

153基于matlab的滚动轴承故障诊断

基于matlab的滚动轴承故障诊断&#xff0c;基于小波包分解&#xff0c;得到数据峭度值&#xff0c;以正常与故障数据峭度差值进行最大尺度重构&#xff0c;对重构信号进行包络谱分析。程序已调通&#xff0c;可直接运行。 153matlab 信号重构 包络谱分析 故障诊断 (xiaohongshu…

Macbook 安装金铲铲之战等 IOS 游戏

前言 Macbook 现在可以玩一下 IOS 系统上的游戏啦&#xff0c;以笔者的 M1 Pro 芯片为例 步骤 一、安装 PlayCover 推荐 Sonama 安装 Nightly 版本 官网地址&#xff1a; https://playcover.io/ Nightly: https://nightly.link/playcover/playcover/workflows/2.nightly_re…

基础小白快速入门python------Python程序设计结构,循环

循环在计算机中&#xff0c;是一个非常重要的概念&#xff0c;是某一块儿代码的不断重复运行&#xff0c;是一种逻辑思维 在编程中的体现&#xff0c;运用数学思维加代码结合加数据&#xff0c;就构成了一个循环。 在Python中&#xff0c;循环主要分为三大类 for循环 while循…

二维图像生成 3D 场景:nerfstudio 帮你简化流程 | 开源日报 No.164

nerfstudio-project/nerfstudio Stars: 7.7k License: Apache-2.0 nerfstudio 是一个友好的 NeRFs 协作工作室。 该项目旨在简化创建、训练和测试 NeRFs 的端到端流程&#xff0c;支持更模块化的 NeRFs 实现&#xff0c;并提供了简单的 API。 其主要功能和优势包括&#xff1…

ABAP 笔记--内表结构不一致,无法更新数据库MODIFY和UPDATE

目录 ABAP 笔记内表结构不一致&#xff0c;无法更新数据库MODIFY和UPDATE ABAP 笔记 内表结构不一致&#xff0c;无法更新数据库 MODIFY和UPDATE 如果是使用MODIFY或者UPDATE

【DDD】学习笔记-什么是模型

从领域驱动的战略设计进入战术设计&#xff0c;简单说来&#xff0c;就是跨过系统视角的限界上下文边界进入它的内部&#xff0c;从分层架构的逻辑分层进入到每一层的内部。在思考内部的设计细节时&#xff0c;首先需要思考的问题就是&#xff1a;什么是模型&#xff08;Model&…

Android 13.0 原生SystemUI下拉通知栏每条通知默认展开

1.前言 在13.0的系统rom原生开发中,在在对SystemUI下拉通知栏做定制的时候,在下拉状态栏的时候,通知栏中最后一条通知默认是收缩的 点击按钮 就会展开 原生系统systemui就是如此,为了更美观 所以要求最后一条通知也默认展开,显得更美观 最终效果图: 2.原生SystemUI下拉通…

Git使用命令大全

命令大全参考阮一峰的博客&#xff0c;根据自己的使用习惯作了调整。 Git常用命令 其他常用的命令 配置Git # 显示当前的Git配置 $ git config --list# 编辑Git配置文件 $ git config -e [--global]# 设置提交代码时的用户信息 $ git config [--global] user.name "[nam…