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)文章
SpringCloud Feign遠(yuǎn)程調(diào)用實(shí)現(xiàn)詳解
Feign是Netflix公司開發(fā)的一個(gè)聲明式的REST調(diào)用客戶端; Ribbon負(fù)載均衡、 Hystrⅸ服務(wù)熔斷是我們Spring Cloud中進(jìn)行微服務(wù)開發(fā)非常基礎(chǔ)的組件,在使用的過程中我們也發(fā)現(xiàn)它們一般都是同時(shí)出現(xiàn)的,而且配置也都非常相似2022-11-11Hibernate Validation自定義注解校驗(yàn)的實(shí)現(xiàn)
這篇文章主要介紹了Hibernate Validation自定義注解校驗(yàn)的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-04-04Java時(shí)間輪算法的實(shí)現(xiàn)代碼示例
本篇文章主要介紹了Java時(shí)間輪算法的實(shí)現(xiàn)代碼示例,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-08-08idea中怎樣創(chuàng)建并運(yùn)行第一個(gè)java程序
這篇文章主要介紹了idea中怎樣創(chuàng)建并運(yùn)行第一個(gè)java程序問題,具有很好的參考價(jià)值,希望對大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-08-08Springboot項(xiàng)目保存本地系統(tǒng)日志文件的實(shí)現(xiàn)方法
這篇文章主要介紹了Springboot項(xiàng)目保存本地系統(tǒng)日志文件的實(shí)現(xiàn)方法,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-04-04解決使用IDEA時(shí)跳轉(zhuǎn)到.class的問題
這篇文章主要介紹了解決使用IDEA時(shí)跳轉(zhuǎn)到.class的問題,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-08-08