Java零拷贝技术实战

文章目录

  • 引入
  • 传统IO
  • 内存映射mmap
  • 文件描述符sendFile
  • 测试
  • 总结

引入

为什么要使用零拷贝技术?
传统写入数据需要4次拷贝,如下图:
在这里插入图片描述

传统IO

import java.io.*;
import java.net.Socket;public class TranditionIOClient {private static final int PORT = 8888;private final static String FILE_NAME = "D:\\test.mp4";// 接收缓冲区大小private static final int BUFFER_SIZE = 1024;public static void main(String[] args) throws Exception {try (Socket socket = new Socket("localhost", PORT);InputStream inputStream = new FileInputStream(FILE_NAME);DataOutputStream dos = new DataOutputStream(socket.getOutputStream());) {byte[] buffer = new byte[BUFFER_SIZE];long readCount = 0;long total = 0;long startTime = System.currentTimeMillis();// 读取文件:从硬盘读取到内存,发生2次copy(DMA拷贝和CPU拷贝)while ((readCount = inputStream.read(buffer)) >= 0) {total += readCount;// 网络发送:从内存到网卡,发生2次copy(DMA拷贝和CPU拷贝)dos.write(buffer, 0, (int) readCount);}System.out.println("TranditionIOClient发送总字节数:" + total + ",耗时:" + (System.currentTimeMillis() - startTime) + " ms");} catch (IOException e) {e.printStackTrace();}}
}

内存映射mmap

在这里插入图片描述

import java.io.FileInputStream;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.SocketChannel;public class MmapClient {private static final int PORT = 8888;private final static String FILE_NAME = "D:\\test.mp4";public static void main(String[] args) throws Exception {try (SocketChannel socketChannel = SocketChannel.open();FileChannel fileChannel = new FileInputStream(FILE_NAME).getChannel()) {socketChannel.connect(new InetSocketAddress("localhost", PORT));socketChannel.configureBlocking(true);long startTime = System.currentTimeMillis();// 获取文件大小long size = fileChannel.size();// 内存映射整个文件,发生3次copy(DMA拷贝和CPU拷贝)ByteBuffer buffer = fileChannel.map(FileChannel.MapMode.READ_ONLY, 0, size);// 发送文件while (buffer.hasRemaining()) {socketChannel.write(buffer);}System.out.println("MmapClient发送总字节数:" + size + ",耗时:" + (System.currentTimeMillis() - startTime) + " ms");} catch (Exception e) {e.printStackTrace();}}
}

文件描述符sendFile

在这里插入图片描述

import java.io.FileInputStream;
import java.net.InetSocketAddress;
import java.nio.channels.FileChannel;
import java.nio.channels.SocketChannel;public class SendFileClient {private static final int PORT = 8888;private final static String FILE_NAME = "D:\\test.mp4";public static void main(String[] args) throws Exception {try (SocketChannel socketChannel = SocketChannel.open();FileChannel fileChannel = new FileInputStream(FILE_NAME).getChannel()) {socketChannel.connect(new InetSocketAddress("localhost", PORT));socketChannel.configureBlocking(true);long startTime = System.currentTimeMillis();long position = 0;// 8MB,与系统缓冲区大小匹配或略小以避免问题long chunkSize = 8 * 1024 * 1024;long size = fileChannel.size();while (position < size) {long bytesRemaining = size - position;// 确保最后一次传输不会超过文件大小long count = Math.min(bytesRemaining, chunkSize);// transferTo⽅法⽤到了零拷⻉,底层是sendfile,发生2次copy(DMA拷贝)long transferCount = fileChannel.transferTo(position, count, socketChannel);if (transferCount == 0) {// 如果一次传输没有发生,可能需要检查连接是否仍然活跃或处理其他错误情况break;}position += transferCount;}System.out.println("SendFileClient发送总字节数:" + size + ",耗时:" + (System.currentTimeMillis() - startTime) + " ms");} catch (Exception e) {e.printStackTrace();}}
}

如果发送的文件不大于8M,则可以简单写,如下:

import java.io.FileInputStream;
import java.net.InetSocketAddress;
import java.nio.channels.FileChannel;
import java.nio.channels.SocketChannel;public class SendFileClient {private static final int PORT = 8888;private final static String FILE_NAME = "D:\\test.mp4";public static void main(String[] args) throws Exception {try (SocketChannel socketChannel = SocketChannel.open();FileChannel fileChannel = new FileInputStream(FILE_NAME).getChannel()) {socketChannel.connect(new InetSocketAddress("localhost", PORT));socketChannel.configureBlocking(true);long startTime = System.currentTimeMillis();// transferTo⽅法⽤到了零拷⻉,底层是sendfile,发生2次copy(DMA拷贝)long transferCount = fileChannel.transferTo(0, fileChannel.size(), socketChannel);System.out.println("SendFileClient发送总字节数:" + transferCount + ",耗时:" + (System.currentTimeMillis() - startTime) + " ms");} catch (Exception e) {e.printStackTrace();}}
}

测试

服务端代码如下:

import java.io.DataInputStream;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;public class Server {private static final int PORT = 8888;// 接收缓冲区大小private static final int BUFFER_SIZE = 1024;public static void main(String[] args) throws Exception {try (ServerSocket ss = new ServerSocket(PORT);) {while (true) {try (Socket s = ss.accept();DataInputStream dis = new DataInputStream(s.getInputStream());) {int byteCount = 0;byte[] bytes = new byte[BUFFER_SIZE];while (true) {int readCount = dis.read(bytes, 0, BUFFER_SIZE);if (readCount == -1) {break;}byteCount = byteCount + readCount;}System.out.println("服务端接受字节数:" + byteCount + "字节");} catch (IOException e) {e.printStackTrace();}}} catch (IOException e) {e.printStackTrace();}}
}

我们都使用了test.mp4进行测试,文件大小500M,测试结果如下:

TranditionIOClient发送总字节数:524288000,耗时:9590 ms
MmapClient发送总字节数:524288000,耗时:1182 ms
SendFileClient发送总字节数:524288000,耗时:983 ms

总结

零拷贝并不是不需要拷贝,而是指计算机执行操作时,不需要将数据从内存复制到应用程序

效率高到低:sendFile>mmap>传统IO

明明传了500M的文件,但实际读出来8M?代码如下:

try (SocketChannel socketChannel = SocketChannel.open();FileChannel fileChannel = new FileInputStream(FILE_NAME).getChannel()) {socketChannel.connect(new InetSocketAddress("localhost", PORT));socketChannel.configureBlocking(true);long startTime = System.currentTimeMillis();// transferTo⽅法⽤到了零拷⻉,底层是sendfile,发生2次copy(DMA拷贝)long transferCount = fileChannel.transferTo(0, fileChannel.size(), socketChannel);System.out.println("SendFileClient发送总字节数:" + transferCount + ",耗时:" + (System.currentTimeMillis() - startTime) + " ms");
} catch (Exception e) {e.printStackTrace();
}// 输出结果:SendFileClient发送总字节数:8388608,耗时:15 ms

原因:由于操作系统的默认socket缓冲区大小限制所导致的。当使用transferTo进行大文件传输时,如果文件大小超过了操作系统为socket分配的缓冲区大小,那么transferTo可能在达到这个限制后停止,因为它试图一次性将数据从文件通道转移到socket通道,但缓冲区不足以容纳整个文件内容。

解决:分批次进行文件传输

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

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

相关文章

sh包装脚本

两个脚本,运行的时间间隔分别是一分钟和五分钟,放入到sh文件中,挂在后代,脚本里面的路径最好是绝对路径。 新建sh文件 新建 run_test.sh 文件,使其可以运行两个不同的 Python 脚本,一个每分钟运行一次,另一个每五分钟运行一次。下面是修改后的 run_test.sh 文件的示例:…

机器学习:人工智能中实现自动化决策与精细优化的核心驱动力

&#x1f512;文章目录: &#x1f4a5;1.概述 ❤️2.机器学习基本原理 &#x1f6e4;️2.1定义与关键概念 &#x1f6e3;️2.2 机器学习算法 ☔3.自动化决策中的机器学习应用 &#x1f6b2;4.精细优化与机器学习的结合 &#x1f44a;5.挑战与前景 &#x1f4a5;1.概述 …

【WEB前端2024】简单几步制作web3d《萌宠星球》智体节点模板(2)

【WEB前端2024】简单几步制作web3d《萌宠星球》智体节点模板&#xff08;2&#xff09; 使用dtns.network德塔世界&#xff08;开源的智体世界引擎&#xff09;&#xff0c;策划和设计《乔布斯超大型的开源3D纪念馆》的系列教程。dtns.network是一款主要由JavaScript编写的智体…

21 使用Hadoop Java API读取序列化文件

在上一个实验中我们筛选了竞赛网站日志数据中2021/1和2021/2的数据以序列化的形式写到了hdfs上。 接下来我们使用Java API 读取序列化的数据保存到磁盘中。 其他命令操作请参考&#xff1a;16 Java API操作HDFS-CSDN博客 1.我直接在上一个项目中test/java目录下创建com.maidu.s…

鸿蒙 Next 模拟器 体验

参加华为社区相关Next 的活动&#xff0c;只要申请通过就可以下载模拟器。整个过程稍微慢些&#xff0c;大家可以根据活动相关信息&#xff0c;加入微信群。跟踪催促进度。争取早日体验 next 。 目前模拟器里边还是空空的&#xff0c;没有什么内置 APP &#xff0c;但是足够大…

net7部署经历

1、linux安装dotnet命令&#xff1a; sudo yum install dotnet-sdk-7.0 或者直接在商店里安装 2、配置反向代理 127.0.0.1:5000》localhost 访问后报错 原因&#xff1a;数据表驼峰名&#xff0c; 在windows的数据表不区分大小写&#xff0c;但是在linux里面是默认区分的&…

【xxl-job | 第三篇】SpringBoot整合xxl-job

文章目录 3.SpringBoot整合xxl-job3.1定时任务服务配置3.1.1导入maven依赖3.1.2yml配置3.1.3XxlJobConfig配置类3.1.4定时任务类 3.2xxl-job配置3.2.1新增执行器3.2.2新增任务3.2.3执行任务3.2.4查看日志3.2.5查看任务后台日志 3.3小结 3.SpringBoot整合xxl-job 3.1定时任务服…

第一个C++项目

文章目录 一、新建项目1.打开软件&#xff0c;选择“创建新项目”2.新建项目栏中&#xff0c;按自己的需求来设置项目模板&#xff0c;项目名称和文件存放位置&#xff0c;设置好后点击“确认”3. 点击“Next”4. 按照自己需求设置&#xff0c;设置完后&#xff0c;点击“Next”…

【数据治理】指标体系

文章目录 1. 如何进行体系化建模2. 高层模型设计3. 派生指标3.1 派生指标体系的架构、概念一种可行的指标构造方式&#xff1a;举例&#xff1a; 3.2 派生指标体系的规范细则 1. 如何进行体系化建模 体系化建模流程图&#xff1a; 指标相关设计流程&#xff1a; 首先&#x…

基于大数据+Hadoop的豆瓣电子图书推荐系统实现

&#x1f339;作者主页&#xff1a;青花锁 &#x1f339;简介&#xff1a;Java领域优质创作者&#x1f3c6;、Java微服务架构公号作者&#x1f604; &#x1f339;简历模板、学习资料、面试题库、技术互助 &#x1f339;文末获取联系方式 &#x1f4dd; 系列文章目录 基于大数…

手动实现简易版RPC(四)

手动实现简易版RPC(四) 往期内容 手动实现简易版RPC&#xff08;一&#xff09;&#xff1a;RPC简介及系统架构 手动实现简易版RPC&#xff08;二&#xff09;&#xff1a;简单RPC框架实现 手动实现简易版RPC(三)&#xff1a;mock数据生成 前言 接上几篇博客我们实现了最…

便签怎么设置不同的标签 便签创建不同分组标签的方法

在日常工作和生活中&#xff0c;便签已成为我随身携带的小助手。每当灵感闪现&#xff0c;或是需要临时记录一些重要事项&#xff0c;我都会随手打开便签&#xff0c;快速记录下来。然而&#xff0c;随着记录的内容越来越多&#xff0c;如何高效地管理和查找这些信息成为了一个…

联丰策略股票炒股市场港股恒生指数止步“10连阳”

查查配港股市场今日未能持续之前的上涨趋势。恒生指数在经历了4月22日至5月6日的“十日连阳”罕见行情后,其反弹动能有所减弱。与此同时,恒生科技指数也遭遇了回调。截至收盘,恒生指数跌0.53%,报收18479.37点;科技指数跌2.13%,报收3922.54点;国企指数跌0.70%,报收6526.67点。 …

【保姆级详细步骤教学用DOSBoxV0.74写出一个汇编语言程序输出Hello World!】

使用任何文本编辑器创建一个名为 HELLO.ASM 的文件&#xff0c;并将以下代码粘贴到文件中&#xff1a; .MODEL SMALL .STACK 100H.DATAMSG DB Hello, World!, $PROMPT DB 13, 10, Press any key to exit..., $.CODEMAIN PROCMOV AX, DATAMOV DS, AXMOV AH, 09HLEA DX, MSGINT …

R语言【Tidyverse、Tidymodel】机器学习

机器学习已经成为继理论、实验和数值计算之后的科研“第四范式”&#xff0c;是发现新规律&#xff0c;总结和分析实验结果的利器。机器学习涉及的理论和方法繁多&#xff0c;编程相当复杂&#xff0c;一直是阻碍机器学习大范围应用的主要困难之一&#xff0c;由此诞生了Python…

2024最详细全面的发卡平台对比调研

最近在调研目前市面上的发卡平台&#xff0c;对一些主流的托管式发卡平台与github上开源的发卡项目做了横向对比&#xff0c;本文主要介绍各自特点以及需要注意避免的坑。 直接上表格&#xff0c;一目了然。 对比独角数卡***发卡/泛发卡平台iDataRiver发卡稳定性/跑路风险自己…

双层胶工艺是什么?

知 识星球&#xff08;星球名&#xff1a; 芯片制造与封测社区&#xff0c;星球号&#xff1a; 63559049&#xff09;里的学员问&#xff1a; 什么是双层胶工艺&#xff1f; 为什么要用双层胶呢&#xff1f; 本图由allresist公司提供&#xff0c;特此鸣谢 为什么要涂双层…

【机器学习与实现】线性回归示例——波士顿房价分析

目录 一、创建Pandas对象并查看数据的基本情况二、使用皮尔逊相关系数分析特征之间的相关性三、可视化不同特征与因变量MEDV&#xff08;房价中值&#xff09;间的相关性四、划分训练集和测试集并进行回归分析 一、创建Pandas对象并查看数据的基本情况 boston.csv数据集下载&a…

Proxmox VE 8 SDN创建VLAN隔离用户网络

作者&#xff1a;田逸&#xff08;formyz&#xff09; 在上一篇文章中&#xff0c;我们用SDN的Simple对租户&#xff08;用户&#xff09;网络实现了隔离功能&#xff0c;但它有个限制&#xff0c;仅仅能在单个物理节点上进行通信&#xff0c;而不能跨越物理节点&#xff08;除…

天诚再出学校物联网锁新品,打造AIoT高校数智化通行解决方案

为进一步提升高职及本科院校校园安防智能化管理水平&#xff0c;助力学校教育信息化转变&#xff0c;校园全场景AIoT解决方案服务商——江苏新巢天诚智能技术有限公司&#xff08;以下简称“天诚”&#xff09;推出系列校园物联网锁新品&#xff0c;为在校师生用户群体提供更多…