Java中三種零拷貝的實(shí)現(xiàn)示例以及對(duì)比詳解
簡介
本文主要是介紹幾種零拷貝的實(shí)現(xiàn)示例,以及與最傳統(tǒng)的做一個(gè)對(duì)比,看看在效率上到底有多大的提升
好了,廢話不多說直接干,本章例子是通過網(wǎng)絡(luò)IO傳輸一個(gè)8M大小的文件,對(duì)比傳輸效率,由于服務(wù)端接收端不需要修改,所以我們先上服務(wù)端代碼:
public static void main(String[] args) throws IOException {
ServerSocket serverSocket = new ServerSocket(8080);
System.out.println("服務(wù)端:等待連接");
Socket accept = serverSocket.accept();
System.out.println("服務(wù)端:" + accept.getRemoteSocketAddress() + "已連接");
File file = new File("C:\\Users\\Administrator\\Desktop\\ioTest.txt");
if(!file.exists()){
file.createNewFile();
}
FileOutputStream fileOutputStream = new FileOutputStream(file);
InputStream bufferedInputStream = accept.getInputStream();
byte[] bytes = new byte[2048];
int read;
while ((read = bufferedInputStream.read(bytes,0,2048)) != -1) {
fileOutputStream.write(bytes);
}
OutputStream outputStream = accept.getOutputStream();
outputStream.write("接收完畢".getBytes());
accept.shutdownOutput();
fileOutputStream.close();
outputStream.close();
bufferedInputStream.close();
accept.close();
}傳統(tǒng)實(shí)現(xiàn)
正常的socket傳輸,耗時(shí):46ms
public static void normal() throws IOException {
Socket socket = new Socket("127.0.0.1", 8080);
OutputStream outputStream = socket.getOutputStream();
InputStream inputStream = socket.getInputStream();
long start = System.currentTimeMillis();
File file = new File("C:\\Users\\Administrator\\Desktop\\222.txt");
FileInputStream fileInputStream = new FileInputStream(file);
byte[] bytes1 = new byte[2048];
while (fileInputStream.read(bytes1, 0, 2048) != -1) {
outputStream.write(bytes1);
}
socket.shutdownOutput();
System.out.println("耗時(shí):" + (System.currentTimeMillis() - start));
byte[] bytes = new byte[1024];
String message = "";
int read;
while ((read = inputStream.read(bytes)) != -1) {
message += new String(bytes, 0, read);
}
System.out.println("服務(wù)端發(fā)來消息->" + message);
inputStream.close();
outputStream.close();
socket.close();
}MMAP
MMAP原理就是建立了一個(gè)文件映射,劃分了一個(gè)虛擬空間,往這個(gè)空間寫數(shù)據(jù),少了一次拷貝
缺點(diǎn):空間有限
實(shí)踐案例:RocketMq
耗時(shí):32ms
public static void mmp() throws IOException {
SocketChannel socketChannel = SocketChannel.open();
socketChannel.connect(new InetSocketAddress("127.0.0.1", 8080));
long start = System.currentTimeMillis();
Path path = Paths.get("C:\\Users\\Administrator\\Desktop\\222.txt");
FileChannel open = FileChannel.open(path, StandardOpenOption.READ);
MappedByteBuffer map = open.map(FileChannel.MapMode.READ_ONLY, 0, open.size());
socketChannel.write(map);
socketChannel.shutdownOutput();
System.out.println("耗時(shí):" + (System.currentTimeMillis() - start));
ByteBuffer allocate = ByteBuffer.allocate(1024);
int read = socketChannel.read(allocate);
if (read > 0) {
allocate.flip();
byte[] bytes = new byte[allocate.remaining()];
allocate.get(bytes);
System.out.println("服務(wù)端發(fā)來消息:" + new String(bytes));
}
socketChannel.close();
}transferTo
原理就是兩個(gè)通道之間直接傳輸數(shù)據(jù),根據(jù)系統(tǒng)支持程度,少了1-2次拷貝
缺點(diǎn):局限于文件通道
實(shí)踐案例:Netty、Kafka
耗時(shí):18ms
public static void transferTo() throws IOException {
SocketChannel socketChannel = SocketChannel.open();
socketChannel.connect(new InetSocketAddress("127.0.0.1", 8080));
long start = System.currentTimeMillis();
Path path = Paths.get("C:\\Users\\Administrator\\Desktop\\222.txt");
FileChannel open = FileChannel.open(path, StandardOpenOption.READ);
long l = open.transferTo(0, open.size(), socketChannel);
socketChannel.shutdownOutput();
System.out.println("耗時(shí):" + (System.currentTimeMillis() - start));
ByteBuffer allocate = ByteBuffer.allocate(1024);
int read = socketChannel.read(allocate);
if (read > 0) {
allocate.flip();
byte[] bytes = new byte[allocate.remaining()];
allocate.get(bytes);
System.out.println("服務(wù)端發(fā)來消息:" + new String(bytes));
}
socketChannel.close();
}堆外內(nèi)存
原理直接使用堆外內(nèi)存,少了一次拷貝
缺點(diǎn):堆外內(nèi)存開啟耗時(shí),此內(nèi)存不受JVM控制,如垃圾回收等
實(shí)踐案例:Netty
耗時(shí):26ms
public static void outSide() throws IOException {
SocketChannel socketChannel = SocketChannel.open();
socketChannel.connect(new InetSocketAddress("127.0.0.1", 8080));
long start = System.currentTimeMillis();
Path path = Paths.get("C:\\Users\\Administrator\\Desktop\\222.txt");
FileChannel open = FileChannel.open(path, StandardOpenOption.READ);
ByteBuffer byteBuffer = ByteBuffer.allocateDirect((int) open.size());
open.read(byteBuffer);
byteBuffer.flip();
socketChannel.write(byteBuffer);
socketChannel.shutdownOutput();
System.out.println("耗時(shí):" + (System.currentTimeMillis() - start));
ByteBuffer allocate = ByteBuffer.allocate(1024);
int read = socketChannel.read(allocate);
if (read > 0) {
allocate.flip();
byte[] bytes = new byte[allocate.remaining()];
allocate.get(bytes);
System.out.println("服務(wù)端發(fā)來消息:" + new String(bytes));
}
socketChannel.close();
}總結(jié)
耗時(shí)統(tǒng)計(jì)不完全準(zhǔn)確,都是多次取平均,具體使用哪種需要看場(chǎng)景來
到此這篇關(guān)于Java中三種零拷貝的實(shí)現(xiàn)示例以及對(duì)比詳解的文章就介紹到這了,更多相關(guān)Java零拷貝方式對(duì)比內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java數(shù)據(jù)機(jī)構(gòu)中關(guān)于并查集的詳解
并查集是一種樹型的數(shù)據(jù)結(jié)構(gòu),用于處理一些不相交集合的合并及查詢問題,如果你還不了解并查集,請(qǐng)看接下來的文章,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值2021-09-09
MyBatis-Plus聯(lián)表查詢(Mybatis-Plus-Join)的功能實(shí)現(xiàn)
mybatis-plus作為mybatis的增強(qiáng)工具,簡化了開發(fā)中的數(shù)據(jù)庫操作,這篇文章主要介紹了MyBatis-Plus聯(lián)表查詢(Mybatis-Plus-Join),需要的朋友可以參考下2022-08-08
Java日常練習(xí)題,每天進(jìn)步一點(diǎn)點(diǎn)(14)
下面小編就為大家?guī)硪黄狫ava基礎(chǔ)的幾道練習(xí)題(分享)。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧,希望可以幫到你2021-07-07
Java自動(dòng)化實(shí)現(xiàn)PowerPoint轉(zhuǎn)換為PDF
在日常的企業(yè)運(yùn)營和個(gè)人工作中,PowerPoint(PPT/PPTX)文件因其強(qiáng)大的演示功能而被廣泛使用,本文將詳細(xì)介紹如何利用 Spire.Presentation for Java 庫,通過簡潔高效的代碼實(shí)現(xiàn) PowerPoint 到 PDF 的轉(zhuǎn)換,需要的可以了解下2025-08-08
基于spring-security 401 403錯(cuò)誤自定義處理方案
這篇文章主要介紹了基于spring-security 401 403錯(cuò)誤自定義處理方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-07-07
springboot整合vue實(shí)現(xiàn)上傳下載文件
這篇文章主要為大家詳細(xì)介紹了springboot整合vue實(shí)現(xiàn)上傳下載文件,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-11-11

