1 零拷贝原理
1.1 传统IO4次拷贝3次切换
2.1优化后3次拷贝2次切换
零拷贝是从操作系统角度看的,是没有CPU拷贝
一次CPU拷贝
mmap将用户空间的一块地址和内核空间的一块地址同时映射到相同的一块物理地址,不管是用户空间还是内核空间都是虚拟地址,最终要通过地址映射映射到物理内存地址。
这种方式避免了内核空间与用户空间的数据交换。IO复用中的epoll函数中就是使用了mmap减少内存拷贝。
2. 代码示例
传统IO
package com.sgg.copyZero;
import java.io.DataInputStream;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
/**
* @description: Java Io 的服务器
* @date : 2020/6/7 11:29
* @author: zwz
*/
public class OldIoServer {
public static void main(String[] args) throws IOException {
ServerSocket serverSocket = new ServerSocket(7001);
while (true) {
Socket socket = serverSocket.accept();
DataInputStream inputStream = new DataInputStream(socket.getInputStream());
try {
byte[] bytes = new byte[4096];
while (true) {
int read = inputStream.read(bytes, 0, bytes.length);
if (read == -1) {
break;
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
package com.sgg.copyZero;
import java.io.DataOutputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.net.Socket;
/**
* @description: Java Io 的服务器
* @date : 2020/6/7 11:29
* @author: zwz
*/
public class OldIoClient {
public static void main(String[] args) throws IOException {
Socket socket = new Socket("127.0.1.1", 7001);
FileInputStream inputStream = new FileInputStream("E:\\resource\\apache-tomcat-v8.5.24.zip");
DataOutputStream dataOutputStream = new DataOutputStream(socket.getOutputStream());
byte[] buffer = new byte[4096];
long readCount = 0;
long total = 0;
long startTime = System.currentTimeMillis();
while ((readCount = inputStream.read(buffer)) > 0) {
total += readCount;
dataOutputStream.write(buffer);
}
System.out.println("发送总字节数:" + total + ",耗时" + (System.currentTimeMillis() - startTime) + "ms");
dataOutputStream.close();
socket.close();
inputStream.close();
}
}
NIO
package com.sgg.copyZero;
import java.io.FileInputStream;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.channels.FileChannel;
import java.nio.channels.SocketChannel;
/**
* @description:
* @date : 2020/6/7 11:42
* @author: zwz
*/
public class NewIOClient {
public static void main(String[] args) throws IOException {
SocketChannel socketChannel = SocketChannel.open();
//向服务器发送链接,一个通道代表一条TCP链接
socketChannel.connect(new InetSocketAddress("127.0.0.1", 7002));
FileChannel fileChannel = new FileInputStream("E:\\resource\\apache-tomcat-v8.5.24.zip").getChannel();
long l = System.currentTimeMillis();
//在linux下一个 transferTo 方法就可以完成传输
//在windows下一次调用 transferTo 只能发送8m,就需要分段传输文件,而且要注意传输的位置
//transferTo底层使用零拷贝
long count = fileChannel.transferTo(0, fileChannel.size(), socketChannel);
System.out.println("发送的总的字节数=" + count + ",耗时:" + (System.currentTimeMillis() - l));
}
}
package com.sgg.copyZero;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.nio.ByteBuffer;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
/**
* @description:
* @date : 2020/6/7 11:42
* @author: zwz
*/
public class NewIOServer {
public static void main(String[] args) throws IOException {
InetSocketAddress inetSocketAddress = new InetSocketAddress(7002);
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
ServerSocket serverSocket = serverSocketChannel.socket();
serverSocket.bind(inetSocketAddress);
ByteBuffer byteBuffer = ByteBuffer.allocate(4096);
while (true) {
SocketChannel socketChannel = serverSocketChannel.accept();
int readCount = 0;
while (-1 != readCount) {
try {
readCount = socketChannel.read(byteBuffer);
} catch (IOException e) {
e.printStackTrace();
}
byteBuffer.rewind(); //倒带 position=0。
}
}
}
}
网友评论