【netty系列-05】深入理解直接内存与零拷贝

news2024/11/25 16:38:51

Netty系列整体栏目


内容链接地址
【一】深入理解网络通信基本原理和tcp/ip协议https://zhenghuisheng.blog.csdn.net/article/details/136359640
【二】深入理解Socket本质和BIOhttps://zhenghuisheng.blog.csdn.net/article/details/136549478
【三】深入理解NIO的基本原理和底层实现https://zhenghuisheng.blog.csdn.net/article/details/138451491
【四】深入理解反应堆模式的种类和具体实现https://zhenghuisheng.blog.csdn.net/article/details/140113199
【五】深入理解直接内存与零拷贝https://zhenghuisheng.blog.csdn.net/article/details/140721001

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

  • 一,Nio直接内存与零拷贝
    • 1,堆内存和直接内存
      • 1.1,直接内存比堆内存快的原因
      • 1.2,直接内存使用的缺陷
    • 2,零拷贝
      • 2.1,Linux DMA
      • 2.2,传统的数据传输
      • 2.3,零拷贝-mmap内存映射
      • 2.4,零拷贝-sendfile
      • 2.5,零拷贝-splice
    • 3,java中使用的零拷贝

一,Nio直接内存与零拷贝

在该系列的第一片文章中,讲解了tcp的基本原理以及实现,在tcp的三次握手中,需要客户端先发起请求给服务端,然后客户端先发送一个 syn 等于1的标志,以及携带一个 seq_no 的序列号给服务端,从而完成第一次握手等等,然而根据tcp的特性,具有 网络重传 ,应答确认功能和封装报文 等功能时如何实现的呢,那么就是通过这个buffer缓冲区 实现的。

操作系统将tcp层以下的协议全部封装,然后通过调用操作系统的socket实现服务端与客户端之间的通信,因此需要通过socket去读取buffer缓冲区中的数据。这里的缓冲区指的是服务端内部之间的缓冲区,和前面提到的反应堆模式中的缓冲区不是同一个,这个缓冲区属于是业务缓冲区

请添加图片描述

1,堆内存和直接内存

通过上图可以得知,客户端在往对端发送数据时,需要先建立socket,然后通过buffer输入缓冲区和输出缓冲区,将数据发送给对端,发送数据的内部细节已由操作系统内部封装。

如在发送数据时,首选需要在用户态中,将数据加载到应用进程的缓冲区中,然后通过Socket建立连接,再调用操作系统的 write 相关的api,然后再将数据发送给内核态的 套接字发送缓冲区 里面,然后再通过一些tcp等协议栈,通过层层协议,通过网络、光缆等将数据发送给对端

请添加图片描述

1.1,直接内存比堆内存快的原因

不管是任何编程语言,都要遵循上面的这套规则,包括java也是。在java中jvm的整体架构如下,可以发现只有堆内存来存储对象,因此一般通过堆内存来存储buffer中的缓存数据

img

但是即使是先使用堆内存来存储buffer中的数据,在jvm内部也做了优化,在上面的这些结构中,加一个 DMA(Direct Memory Access) ,即直接内存的区域,如果是先用堆内存先存数据,也会将堆内存的数据复制(拷贝)到直接内存中,然后再通过直接内存区域中的数据,发送到内核的缓冲区,再发送到对端中。

jvm中使用直接内存的原因如下,首先是由于堆内存中会gc操作,那么在涉及对象复制或者空间碎片化管理等的时候,会将对象的位置进行移动,比如原先在100号位置,在调用write操作的时候,操作系统默认就会去读取堆内存上100号位置的数据,但是如果在读的时候发生了gc,导致堆内存上的数据发生了移动,那么原先100号的数据被挪到了50号,那么就会导致读取不到数据,或者读取不是该读取的数据 ,因此jvm为了优化这个堆内存读取buffer数据会出错的缺陷,专门开辟了一个新的直接内存,用于存储buffer数据

因此在做网络通信的时候,优先将存储buffer的内存改使用直接内存存储,堆存储需要一个数据拷贝到直接内存的时间,而直接内存不需要,因此使用直接内存是快于堆内存的。

1.2,直接内存使用的缺陷

在开发时直接使用直接内存确实爽,也可以加快整体效率,尤其时涉及到io密集型的操作时,可以显著的提升性能,但是使用直接内存也存在一些缺陷。

  • 如需要开发者手动的分配和释放内存,并且内存的开辟和释放不受jvm控制,也就是说内部需要涉及到底层系统的资源分配
//定义一个1m的直接内存
ByteBuffer allocate = ByteBuffer.allocate(1024 * 1024);
//直接内存空间释放
allocate.clear();
  • 其次就是如果内存不受jvm控制,那么调试会很困难,如一些内存泄漏无法保证,并且排查和调试这种问题也比较困难,一般的java监控工具都不能直接有效的监视直接内存的情况

  • 最后就是这个系统的资源利用率,由于并不受jvm控制,因此会增加操作系统的资源消耗,从而影响其他进程

因此开发者在考虑使用这个直接内存时,需要权衡利弊,结合实际的应用场景选择。

2,零拷贝

2.1,Linux DMA

在最早期的数据拷贝的方式如下,需要先通过cpu的介入,通过cpu的调度将磁盘加载到内存中,如需要从输入设备读取数据,然后将其写入内存。但是cpu主要做的大量的运算操作,如果让cpu长期的出去干这种没有含金量的事情,让cpu长时间的等待,会影响整个机器的性能,并且有损cpu的性能和价值,因此原始数据拷贝的方式相对较慢

为了解决上面的缺陷,减少cpu的负担,并且提高数据的传输效率,因此在硬件层面,就增加一块硬件,专门用于处理这种直接代替cpu去读取数据,加载内存,这个就叫做Linux的直接内存DMA。如增加一个磁盘驱动器,专门用于处理这种数据的读取和加载,而cpu就不需要像之前一样去调用输入输出设备,只需要给这个DMA发送一个指令即可,然后剩下的就交给DMA。DMA目前已经适用于多种计算机的硬件设备,如 硬盘驱动器、声卡、网络设备等

请添加图片描述

2.2,传统的数据传输

Linux在引入了DMA之后,系统可以更加的高速的对数据进行读写,并且可以高效的处理一些音频视频的数据。

在传统的数据传输引入了DMA之后,其数据传输效率更加高效,接下来如看一段简单的代码表示数据的读取和传输,就是先调用操作系统的api读取数据,然后将最终读取到的数据通过Socket发送到对端

//读取数据
FileBuffer buffer = new File.read()
//将数据发送到对端
Socket.send(buffer)    

上面这段代码虽然简单,但是在整个计算机内部执行的流程比较复杂,其主要流程如下:

  • 如在执行第一条语句的时候,此时还在用户态,通过程序计数器执行到改行代码,然后调用操作系统的api,此时需要将用户态切换为内核态,然后先从磁盘中将数据通过DMA的方式拷贝到文件读取的buffer缓冲区中,然后将文件缓冲区的数据投通过cpu拷贝到执行这条语句的jvm进程中,此时操作系统将内核态切换为用户态

  • 此时执行第二条语句,首先需要将进程中的数据通过CPU拷贝到Socket对应的buffer缓冲区中,又因为socket对应的send方法是操作系统层面的api,因此有需要从用户态切换到内核态,最后将socket中的buffer缓冲数据通过DMA拷贝的方式加入到网络设备对应的缓冲区中

请添加图片描述

总结来说就是需要4次用户态和内核态之间的上下文切换,4次数据的拷贝,分别是两次DMA拷贝,两次cpu拷贝。传统的数据传输虽然也是引入了DMA拷贝,但是依旧存在着两次CPU的拷贝,并且上下文切换也比较频繁

2.3,零拷贝-mmap内存映射

为了解决传统传输多次cpu拷贝带来的性能问题,在后面的优化中,采用了一种mmap内存映射的方式,就是提前对磁盘中数据的位置做一个映射,对应进程中的应用程序只需要通过这个地址直接去磁盘中拉取数据就行,从而不需要将数据先读取到buffer缓冲区中,同时也减少了一次cpu的拷贝

请添加图片描述

通过mmap的方式,减少了一次cpu的拷贝,因此只需要一次cpu拷贝,2次DMA拷贝,但是依旧需要4次上下文切换

2.4,零拷贝-sendfile

随着linux的不断升级,mmap的缺陷也在不断优化,在linux2.1的版本中,引入了sendfile的零拷贝方式,相对于mmap方式,sendfile同时在上下文切换和cpu拷贝时都做了优化。

从上下文切换来讲:

  • 在调用操作系统的read方法之后,其数据不需要再返回到用户态中,因此在整个传输流程结束之后再切换为用户态,因此在上下文切换只需要两次,比上面的减少了两次,剩下的两次上下文切换时必不可少的

从拷贝的角度来看:

  • 此时需要看该操作系统是否支持DMA拷贝,如果支持DMA拷贝,那么下图中的CPU拷贝可以直接不需要,就只需要两步:1、从磁盘中将数据通过DMA拷贝到文件缓冲区中,2、网络设备缓冲区直接读取文件缓冲区的数据 。此时只需要两次DMA拷贝,不需要CPU拷贝
  • 如果该操作系统不支持使用DMA拷贝,那么就需要一次CPU拷贝,流程如下:1、从磁盘中将数据通过DMA拷贝到文件缓冲区中,2、文件缓冲区的数据通过CPU拷贝到socket中的buffer缓冲区,3、网络设备缓冲区直接读取文件缓冲区的数据 。此时需要两次DMA拷贝,1次cpu拷贝

请添加图片描述

总结就是只需要两次上下文切换,两次DMA拷贝,0次或者1次的CPU拷贝

2.5,零拷贝-splice

在linux2.6版本中,又迎来了第三次优化,由于sendfile还需要根据特定的操作系统以及对应的环境才能不用cpu的拷贝,为了解决这种出现概率性的问题,因此splice出现了

其原理和sendfile很像,只是在文件缓冲区和socket缓冲区中加了一个管道,这就相当于把这两个东西合二为一了,也就相对于磁盘拷贝出去的区域和网络设备缓冲区拷贝进来的区域是同一个区域,因此在完全解决了不需要cpu拷贝。

请添加图片描述

相对于sendfile,这种零拷贝方式不会局限于某个操作系统或者环境,能保证不需要cpu拷贝,只需要两次上下文切换和两次DMA切换

3,java中使用的零拷贝

在java中,目前暂时没有支持splice方式的零拷贝,即目前只支持mmap方式和sendfile方式。在java的各个中间件中,如kafka,nio等等这些地方用到了零拷贝。kaka在生产者端使用的是mmap,结合顺序写可以每秒处理百万级别的业务,生产端使用的是sendfile。nio中在读取数据时也使用mmap,在数据传输中也用了sendfile方式

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

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

相关文章

谷歌再被OpenAI截胡?训练数学AI拿下IMO银牌,不及SearchGPT放了空响..

昨夜谷歌振臂高呼:我们新的数学AI,能在IMO数学竞赛达到银牌水平! 但就在谷歌发文的几个小时后,OpenAI就偷了谷歌的家: 发布新的搜索方式SearchGPT ,剑指挑战谷歌的搜索引擎。 谷歌双模型并用攻克数学难题…

如何利用业余时间做副业,在家里赚钱,来增加收入

人一生每个阶段都会有压力和烦恼,中年人更是如此。 上有老下有小,生活的重担都在一个人身上,压得人喘不过气,这些都需要钱,仅靠工资已经很难维持一家人的开支了。 所以很多人打算利用业余时间做副业,来增加…

为什么多数大数据治理项目都是失败的?Gartner调查失败率超过90%

引言:随着数据规模的爆发式增长、数据价值的凸显以及数据治理的必要性。在政策的推动、市场需求的拉动以及技术进步的支撑下,大数据治理正成为推动企业数字化转型、提升数据价值的重要手段。企业希望通过大数据治理提升数据利用率和数据价值,…

学习记录——day17 数据结构 队列 链式队列

队列介绍 1、队列也是操作受限的线性表:所有操作只能在端点处进行,其删除和插入必须在不同端进行 2、允许插入操作的一端称为队尾,允许删除操作的一端称为队头 3、特点:先进先出(FIFO) 4、分类: 顺序存储的栈称为顺序栈 链式存储的队列&a…

免费【2024】springboot 成都奥科厨具厂产品在线销售系统设计与实现

博主介绍:✌CSDN新星计划导师、Java领域优质创作者、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和学生毕业项目实战,高校老师/讲师/同行前辈交流✌ 技术范围:SpringBoot、Vue、SSM、HTML、Jsp、PHP、Nodejs、Python、爬虫、数据可视化…

将手机作为服务器运行docker服务

前言 目前手机的配置并不低,即使是2019年生产的一加七Pro,配置也有12256,CPU是骁龙855,作为服务器运行着配置绰绰有余了,二手的价格现在是400左右也能接受。相对于是自带ups电源的便携低耗docker服务器,还…

C++ unordered_map与unordered_set的模拟实现

目录 0.前言 1.哈希表(HashTable)设计 1.1设计思想 1.2 HashTable.h 1.3设计思路 2.unordered_map封装 2.1 UnorderedMap.h 2.2代码解释 2.3测试函数 3.unordered_set封装 3.1 UnorderedSet.h 3.2代码解释 3.3测试函数 4.结语 (图像由AI生成&…

项目打包与运行

前端运行时必须有与后端相同的数据库版本,数据库账号密码 右侧maven -> 展开要打包的项目 -> 生命周期 -> 双击package 打包好之后在target目录下 右键打开 在资源目录下输入cmd,执行以下命令即可运行(端口号为yml文件…

人流量为王:背后的赚钱密码深度解析

在当今商业世界中,“人流量为王”这一理念被广泛认可和奉行。但你是否认真思考过,这简单的四个字背后,究竟隐藏着怎样复杂而精妙的赚钱逻辑? 一、人流量意味着潜在客户的聚集 想象一下繁华的商业街,熙熙攘攘的人群穿梭…

7月26日JavaSE学习笔记

反射 Java是面向对象的,有对象必须先有类, 有static修饰类的属性和方法;在Java中存储了类的内容,这个内容也应该是一个对象;Java中每一个用到的类都会加载一块内存,这每一块内存都是一个对象;这…

学习周报:文献阅读+HEC RAS案例

目录 摘要 Abstract 文献阅读:通过HEC RAS软件为罗马尼亚布加勒斯特市的Dmbovița河水管理的水力模型 文献摘要 讨论|结论 理论知识 边界条件计算 流量计算方式 曼宁公式 (Mannings Equation) 连续性方程 (Continuity Equation) 能量方程 (Energy Equatio…

EB Tresos 基于S32K3芯片 ICU模块实现gpio外部中断配置[后续更新实现icu模块的其他功能]

环境:eb tresos 27.0.1 port 模块配置: 选择一个具有erq功能的引脚并配置为erq功能。如下我选择的是 PTB0 -EIRQ[8] - SIUL2_EXT_IRQ_8_15_ISR Platform 模块配置 在这个模块中配置中断的开关以及中断句柄 ICU模块配置 具体配置参考博客:…

【python】python大学排名数据抓取+可视化(源码+数据集+可视化+论文)【独一无二】

👉博__主👈:米码收割机 👉技__能👈:C/Python语言 👉公众号👈:测试开发自动化【获取源码商业合作】 👉荣__誉👈:阿里云博客专家博主、5…

深度强化学习 ②(DRL)

参考视频:📺王树森教授深度强化学习 前言: 最近在学习深度强化学习,学的一知半解😢😢😢,这是我的笔记,欢迎和我一起学习交流~ 这篇博客目前还相对比较乱,后面…

黑马Java零基础视频教程精华部分_5_面向对象综合练习

系列文章目录 文章目录 系列文章目录一、文字版格斗游戏二、文字版格斗游戏进阶版三、对象数组练习1、对象数组1先学习一下键盘录入。注意:两套体系不能混用 对象数组2对象数组3对象数组4对象数组5 一、文字版格斗游戏 GameTes.javat代码如下: package …

[数通网络基础]——广播域与路由器

广播域 广播域概述 广播域是指网络中能接收到同一广播消息的所有设备的集合。 广播域的大小会影响网络的性能和效率。当同一个广播域内广播报文过多时,会对局域网造成干扰,导致网络延迟,网络拥塞(上网卡,上网慢&…

hot100-3滑动窗口

3无重复字符得最长字串 438找出字符串中得所有字母异位词 遇到没有限制字母排列方式的,都可以考虑维护一个charCode数组 和第567题相似 567字符串得排列(和438一个思路)

docker dotnet-dump离线部署

1.下载指定dotnet版本的dotnet-dump 示例地址: https://www.nuget.org/packages/dotnet-dump/3.1.141901#dependencies-body-tab 我本地测试的是netcore 3.1 2. 在本地解压 将文件解压出来。看到any目录,能看到我们要用的dotnet-dump文件 3. 将tools/netcoreapp2.…

AccessLog| 一款开源的日志分析系统

前言 ClkLog作为分析系列产品中的前端数据分析系统,通过采集前端应用数据进行用户行为分析。其社区版从23年9月发布至今已有近一年,商业版也上线快半年,感谢大家一直以来的关注和支持,ClkLog会继续做好产品升级与服务,…

算法-----递归~~搜索~~回溯(宏观认识)

目录 1.什么是递归 1.1二叉树的遍历 1.2快速排序 1.3归并排序 2.为什么会用到递归 3.如何理解递归 4.如何写好一个递归 5.什么是搜索 5.1深度(dfs)优先遍历&优先搜索 5.2宽度(bfs)优先遍历&优先搜索 6.回溯 1.什…