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通道,但缓冲区不足以容纳整个文件内容。

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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【Java】/*方法的递归*/

目录 一、递归的概念 二、递归执行过程分析 三、递归练习 3.1 按顺序打印一个数字的每一位&#xff0c;例如123打印出1 2 3 3.2 递归求 1 2 3 ... n 的和 3.3 输入一个非负整数&#xff0c;返回组成它的数字之和&#xff0c;例如123&#xff0c;得123 3.4 求斐波那契…

工业无风扇计算机的优点

无风扇计算机往往采用紧凑且密封的外形&#xff0c;使其坚固耐用&#xff0c;使其能够在需要现场工程师进行维护之前承受恶劣的环境数年。机载移动部件较少或没有移动部件会降低组件无法按预期运行的可能性&#xff0c;或者更糟糕的是发生故障和损坏。采用工业组件和较低的散热…

mysql,sqlserver数据库查询表,获得表结构,结构类型说明,获得这些数据,可以拿去创建表

mysql&#xff0c;sqlserver数据库查询表&#xff0c;获得表结构&#xff0c;结构类型说明&#xff0c;获得这些数据&#xff0c;可以拿去创建表 //表名p_order select * from information_schema.COLUMNS where TABLE_NAMEp_order;1、TABLE_CATALOG &#xff0c;nvarchar(128…

手撸XXL-JOB(二)——定时任务管理

在上一节中&#xff0c;我们介绍了SpringBoot中关于定时任务的执行方式&#xff0c;以及ScheduledExecutorService接口提供的定时任务执行方法。假设我们现在要写类似XXL-JOB这样的任务调度平台&#xff0c;那么&#xff0c;对于任务的管理&#xff0c;是尤为重要的。接下来我们…

Kerberos修改协议为TCP

部署前 修改模板/home/cloud-setup/deploy-forklift/mids/forklift-basic/kde/v1.0/impl/plays/roles/krb5-client/templates/krb5.conf.j2 添加如下参数 udp_preference_limit 1 部署后 界面修改 添加如下参数&#xff0c;并勾选下发配置按钮&#xff0c;重启刷新服务

中电金信:专题报告·商业银行对公数字化转型体系架构及实践拆解

当今&#xff0c;数字化转型已然成为商业银行发展的关键动力&#xff0c;在这个数字时代&#xff0c;对公业务数字化转型更是势在必行。 基于此&#xff0c;中电金信发布《商业银行对公数字化转型专题报告》&#xff08;简称《报告》&#xff09;&#xff0c;针对对公数字化转型…

MATLAB科技绘图与数据分析

大家好&#xff0c;我是爱编程的喵喵。双985硕士毕业&#xff0c;现担任全栈工程师一职&#xff0c;热衷于将数据思维应用到工作与生活中。从事机器学习以及相关的前后端开发工作。曾在阿里云、科大讯飞、CCF等比赛获得多次Top名次。现为CSDN博客专家、人工智能领域优质创作者。…

探索执法部门如何在不依赖面部识别的情况下追踪感兴趣的人

概述 视频证据在犯罪调查中的重要性正日益凸显&#xff0c;其数量之多已经达到了前所未有的水平。据美国司法援助局&#xff08;Bureau of Justice Assistance, BJS&#xff09;的数据显示&#xff0c;大约80%的犯罪案件都涉及到某种形式的视频证据&#xff0c;并且这一比例还…

Golang | Leetcode Golang题解之第80题删除有序数组中的重复项II

题目&#xff1a; 题解&#xff1a; func removeDuplicates(nums []int) int {n : len(nums)if n < 2 {return n}slow, fast : 2, 2for fast < n {if nums[slow-2] ! nums[fast] {nums[slow] nums[fast]slow}fast}return slow }

基于springboot+vue+Mysql的在线答疑系统

开发语言&#xff1a;Java框架&#xff1a;springbootJDK版本&#xff1a;JDK1.8服务器&#xff1a;tomcat7数据库&#xff1a;mysql 5.7&#xff08;一定要5.7版本&#xff09;数据库工具&#xff1a;Navicat11开发软件&#xff1a;eclipse/myeclipse/ideaMaven包&#xff1a;…

智慧安防系统:构建更安全的社区环境

随着科技的不断进步&#xff0c;人们的生活质量得到了显著提高。然而&#xff0c;与此同时&#xff0c;社会治安问题也日益凸显。为了维护社会的和谐稳定&#xff0c;提高人们的生活安全感&#xff0c;智慧安防系统应运而生。本文将为您详细介绍智慧安防系统的项目背景、需求分…

探秘WebSQL:轻松构建前端数据库

欢迎来到我的博客&#xff0c;代码的世界里&#xff0c;每一行都是一个故事 探秘WebSQL&#xff1a;轻松构建前端数据库 前言WebSQL简介WebSQL的基本操作WebSQL的实际应用WebSQL的局限性和替代方案 前言 在Web的世界里&#xff0c;我们总是追求更好的用户体验和更快的响应速度…

IT行业找工作十面十败,不妨试试鸿蒙开发岗~

近期某脉上看到这样一则帖子&#xff0c;讨论的非常激烈&#xff01; 相信也有不少人有和他这情况类似&#xff0c;像他这种失业的状态&#xff0c;近两年大家或多或少都深有体验。由于互联网行业进过了十几年的快速发展&#xff0c;从2G→3G→4G→5G&#xff0c;在这个期间人们…

嵌入式单片机笔试题

DC-DC 和 LDO两者有何区别&#xff1f; DC-DC转换器&#xff08;直流-直流转换器&#xff09;和LDO&#xff08;低压差线性稳压器&#xff09;都是用于电源管理的设备&#xff0c;但它们在原理和特性上有一些显著的区别&#xff1a; 原理&#xff1a; DC-DC转换器通过改变输…

Spring AI开发前期开发指导(maven依赖下载问题解决)

文章目录 说明开发条件网络环境准备本地环境准备开发工具准备 特殊说明maven配置项目jar一致下载错误解决可行的版本搭配 说明 动力节点视频教程地址&#xff0c;本文章学习该教程&#xff0c;同时说明的maven配置问题导致的项目依赖下载失败的问题和其他问题的记录。 开发条…

android自定义view仿微信联系人列表

说明&#xff1a;最近碰到一个需求&#xff0c;弄一个类似国家或省份列表&#xff0c;样式参照微信联系人 文件列表&#xff1a; step1:主界面 加载列表数据~\app\src\main\java\com\example\iosdialogdemo\MainActivity.java step2:右侧列表数据排序~\app\src\com\example\io…

2024年网络安全岗位缺口已达100万,你该不会还不知道如何入门吧?

我发现最近安全是真的火&#xff0c;火到不管男女老少都想入门学一下。但是&#xff0c;要是真的问起他们&#xff0c;“你觉得网络安全是什么&#xff1f;为什么想学&#xff1f;”&#xff0c;十个人里不见得有一个人能逻辑清晰、态度坚定地回答出来。 作为一个时刻关注行业…

识别公式的网站都有哪些?分享3个专业的工具!

在数字化时代&#xff0c;公式识别已成为一项重要技能。无论是学生、教师还是科研人员&#xff0c;都可能需要借助公式识别网站来快速准确地获取公式信息。那么&#xff0c;市面上到底有哪些值得一试的识别公式网站呢&#xff1f;本文将为您一一揭晓。 FUNAI FUNAI的公式识别功…

Git 基础使用(1) 入门指令

文章目录 Git 作用Git 安装Git 使用Git 仓库配置Git 工作原理Git 修改添加Git 查看日志Git 修改查询Git 版本回退 概念补充 Git 作用 Git 是一种分布式版本控制系统&#xff0c;它旨在追踪文件和文件夹的更改&#xff0c;并协助多人协作开发项目。 Git 安装 &#xff08;Lin…

7nm项目之模块实现——02 Placeopt分析

一、Log需要看什么 1.log最后的error 注意&#xff1a;warnning暂时可以不用过于关注&#xff0c;如果特别的warning出现问题&#xff0c;在其他方面也会体现 2.run time 在大型项目实际开发中&#xff0c;周期一般较长&#xff0c;可能几天过这几周&#xff0c;所以这就需要…