Java中三種零拷貝的實現(xiàn)示例以及對比詳解
簡介
本文主要是介紹幾種零拷貝的實現(xiàn)示例,以及與最傳統(tǒng)的做一個對比,看看在效率上到底有多大的提升
好了,廢話不多說直接干,本章例子是通過網絡IO傳輸一個8M大小的文件,對比傳輸效率,由于服務端接收端不需要修改,所以我們先上服務端代碼:
public static void main(String[] args) throws IOException { ServerSocket serverSocket = new ServerSocket(8080); System.out.println("服務端:等待連接"); Socket accept = serverSocket.accept(); System.out.println("服務端:" + 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)實現(xiàn)
正常的socket傳輸,耗時: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("耗時:" + (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("服務端發(fā)來消息->" + message); inputStream.close(); outputStream.close(); socket.close(); }
MMAP
MMAP原理就是建立了一個文件映射,劃分了一個虛擬空間,往這個空間寫數(shù)據,少了一次拷貝
缺點:空間有限
實踐案例:RocketMq
耗時: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("耗時:" + (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("服務端發(fā)來消息:" + new String(bytes)); } socketChannel.close(); }
transferTo
原理就是兩個通道之間直接傳輸數(shù)據,根據系統(tǒng)支持程度,少了1-2次拷貝
缺點:局限于文件通道
實踐案例:Netty、Kafka
耗時: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("耗時:" + (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("服務端發(fā)來消息:" + new String(bytes)); } socketChannel.close(); }
堆外內存
原理直接使用堆外內存,少了一次拷貝
缺點:堆外內存開啟耗時,此內存不受JVM控制,如垃圾回收等
實踐案例:Netty
耗時: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("耗時:" + (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("服務端發(fā)來消息:" + new String(bytes)); } socketChannel.close(); }
總結
耗時統(tǒng)計不完全準確,都是多次取平均,具體使用哪種需要看場景來
到此這篇關于Java中三種零拷貝的實現(xiàn)示例以及對比詳解的文章就介紹到這了,更多相關Java零拷貝方式對比內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
Spring boot + mybatis + orcale實現(xiàn)步驟實例代碼講解
這篇文章主要介紹了Spring boot + mybatis + orcale的實現(xiàn)步驟實例代碼講解,需要的朋友可以參考下2017-12-12