Java圖文并茂詳解NIO與零拷貝
零拷貝指的是沒有CPU拷貝,并不是不拷貝;減少上下文切換
一、概念說明
1、傳統(tǒng)IO
需要4次拷貝,3次上下文切換

2、mmap
mmap 通過內(nèi)存映射,將文件映射到內(nèi)存緩沖區(qū),同時(shí)用戶空間可以共享內(nèi)存緩沖區(qū)的數(shù)據(jù),減少內(nèi)核空間到用戶空間的拷貝
需要3次拷貝,3次上下文切換

3、sendfile
Linux 2.4 避免了從內(nèi)核緩沖區(qū)到Socket Buffer的拷貝,直接拷貝到協(xié)議棧,從而減少一次數(shù)據(jù)拷貝
需要2次拷貝,3次上下文切換

4、mmap與sendfile
mmap適合小數(shù)據(jù)量讀寫,sendfile適合大文件傳輸
mmap需要4次上下文切換,3次數(shù)據(jù)拷貝;sendfile需要3次上下文切換,最少2次數(shù)據(jù)拷貝
send可用利用DMA方式,減少CPU拷貝,mmap則不能(必須從內(nèi)核拷貝到Socket緩沖區(qū))
二、傳統(tǒng)IO傳輸文件代碼示例
1、服務(wù)端代碼
import java.io.DataInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
public class BIOServer {
public static void main(String[] args) throws IOException {
ServerSocket serverSocket = new ServerSocket(7000);
while (true) {
Socket socket = serverSocket.accept();
DataInputStream dataInputStream = new DataInputStream(socket.getInputStream());
try {
long total = 0;
byte[] bytes = new byte[4096];
FileOutputStream fileOutputStream = new FileOutputStream("d:\\temp\\04.zip");
while (true) {
int read = dataInputStream.read(bytes, 0, bytes.length);
if (read == -1) {
//文件讀取結(jié)束,退出循環(huán)
break;
}
total += read;
fileOutputStream.write(bytes, 0, read);
}
System.out.println("收到客戶端發(fā)送文件,總字節(jié)數(shù):" + total);
fileOutputStream.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}2、客戶端代碼
import java.io.DataOutputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.net.Socket;
public class BIOClient {
public static void main(String[] args) throws IOException {
Socket socket = new Socket("127.0.0.1", 7000);
FileInputStream fileInputStream = new FileInputStream("d:\\temp\\03.zip");
DataOutputStream dataOutputStream = new DataOutputStream(socket.getOutputStream());
byte[] bytes = new byte[4096];
long readCount;
long total = 0;
long start = System.currentTimeMillis();
while ((readCount = fileInputStream.read(bytes)) >= 0) {
total += readCount;
dataOutputStream.write(bytes);
}
System.out.println("發(fā)送總字節(jié)數(shù):" + total + ", 總耗時(shí):" + (System.currentTimeMillis() - start));
dataOutputStream.close();
socket.close();
fileInputStream.close();
}
}3、控制臺出輸出
測試發(fā)送9M的壓縮文件,耗時(shí)在26ms左右
發(fā)送總字節(jié)數(shù):9428963, 總耗時(shí):26
三、NIO傳輸文件代碼示例
1、服務(wù)端代碼
package com.hj.io.nio.zero;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
public class NIOServerFile {
public static void main(String[] args) throws IOException {
InetSocketAddress address = new InetSocketAddress(7000);
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
serverSocketChannel.socket().bind(address);
//創(chuàng)建buffer
ByteBuffer byteBuffer = ByteBuffer.allocate(4096);
while (true) {
//等待客戶端鏈接
SocketChannel socketChannel = serverSocketChannel.accept();
System.out.println("收到客戶端鏈接");
int total = 0;
FileOutputStream fileOutputStream = new FileOutputStream("d:\\temp\\05.zip");
//循環(huán)讀取數(shù)據(jù),并存儲到硬盤
while (true) {
try {
int readCount = socketChannel.read(byteBuffer);
if (readCount == -1) {
//文件讀取結(jié)束
break;
}
total += readCount;
fileOutputStream.write(byteBuffer.array(),0,readCount);
//將buffer倒帶
byteBuffer.rewind();
} catch (IOException e) {
break;
}
}
System.out.println("收到客戶端發(fā)送文件,總字節(jié)數(shù):" + total);
fileOutputStream.close();
}
}
}2、客戶端代碼
import java.io.FileInputStream;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.channels.FileChannel;
import java.nio.channels.SocketChannel;
public class NIOClientFile {
public static void main(String[] args) throws IOException {
//打開一個(gè)SocketChannel并鏈接到服務(wù)器端
SocketChannel socketChannel = SocketChannel.open(new InetSocketAddress("127.0.0.1", 7000));
//打開一個(gè)文件
FileChannel fileChannel = new FileInputStream("d:\\temp\\03.zip").getChannel();
//發(fā)送文件到服務(wù)器
long start = System.currentTimeMillis();
//在linux下,一次transferTo調(diào)用就可以完成傳輸
//在windows下,一次transferTo調(diào)用最多只能傳8M,大文件需要分段傳輸,需要注意傳輸位置
//transferTo底層使用零拷貝
long total = fileChannel.size();
long sended = 0;
while (sended < total) {
//從上一次傳輸位置繼續(xù)發(fā)送
long lenth = fileChannel.transferTo(sended, fileChannel.size(), socketChannel);
sended += lenth;
}
System.out.println("發(fā)送總字節(jié)數(shù):" + sended + ",總耗時(shí):" + (System.currentTimeMillis() - start));
//關(guān)閉channel
fileChannel.close();
socketChannel.close();
}
}3、控制臺出輸出
測試發(fā)送9M的壓縮文件,耗時(shí)在16ms左右
發(fā)送總字節(jié)數(shù):9428963,總耗時(shí):16
四、總結(jié)
使用零拷貝傳輸,性能明顯高于傳統(tǒng)IO傳輸
到此這篇關(guān)于Java圖文并茂詳解NIO與零拷貝的文章就介紹到這了,更多相關(guān)Java NIO與零拷貝內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java實(shí)現(xiàn)ip地址和int數(shù)字的相互轉(zhuǎn)換
這篇文章主要介紹了Java實(shí)現(xiàn)ip地址和int數(shù)字的相互轉(zhuǎn)換,幫助大家更好的利用Java處理數(shù)據(jù),感興趣的朋友可以了解下2020-09-09
Eclipse?2022?設(shè)置中文漢化的超詳細(xì)圖文教程
這篇文章主要介紹了Eclipse?2022?設(shè)置中文漢化的超詳細(xì)圖文教程,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-03-03
Java設(shè)計(jì)模式之代理模式(Proxy模式)介紹
這篇文章主要介紹了Java設(shè)計(jì)模式之代理模式(Proxy模式)介紹,本文講解了為什么要使用代理模式、如何使用代理模式等內(nèi)容,需要的朋友可以參考下2015-03-03
Java文件讀寫IO/NIO及性能比較詳細(xì)代碼及總結(jié)
這篇文章主要介紹了Java文件讀寫IO/NIO及性能比較詳細(xì)代碼及總結(jié),具有一定借鑒價(jià)值,需要的朋友可以參考下。2017-12-12
Java泛型T,E,K,V,N,?與Object區(qū)別和含義
Java?泛型(generics)是?JDK?5?中引入的一個(gè)新特性,?泛型提供了編譯時(shí)類型安全檢測機(jī)制,該機(jī)制允許程序員在編譯時(shí)檢測到非法的類型。本文將詳細(xì)講講Java泛型T、E、K、V、N、?和Object區(qū)別和含義,需要發(fā)可以參考一下2022-03-03
IDEA中實(shí)現(xiàn)springboot熱部署方式
在IDEA中實(shí)現(xiàn)SpringBoot的熱部署可以通過修改設(shè)置來完成,首先在設(shè)置中搜索Compiler,并勾選Build project automatically,然后進(jìn)入Advanced Settings,勾選Allow auto-make to start even if developed application is currently running2024-09-09

