Java IO 模型
文章目录
3.8 NIO 与 零拷贝
零拷贝从操作系统角 度,是没有cpu拷贝
3.8.1 基本介绍
正常IO:file -> page cache -> application cache -> socket cache -> nic
零拷贝:file - > page cache -> nic
零拷贝优势:
○ 减少的数据复制,减轻内存与CPU压力
○ 减少用户态与内核态之间的来回切换
java 中常用的零拷贝 mmap(内存映射)和 sendFile
3.8.2 传统IO
public class NIOZeroCopyOldServer {
public static void main(String[] args) throws Exception {
ServerSocket serverSocket = new ServerSocket(8090);
while (true) {
Socket socket = serverSocket.accept();
InputStream inputStream = socket.getInputStream();
byte[] buffer = new byte[8 * 1024];
while ((inputStream.read(buffer, 0, buffer.length)) != -1) {
}
}
}
}
public class NIOZeroCopyOldClient {
public static void main(String[] args) {
Socket socket = null;
FileInputStream inputStream = null;
DataOutputStream outputStream = null;
try {
socket = new Socket("127.0.0.1", 8090);
outputStream = new DataOutputStream(socket.getOutputStream());
inputStream = new FileInputStream(new File("files//Typora.zip"));
byte[] buffer = new byte[8 * 1024];
long read;
long total = 0; //统计文件字节数
long startTime = System.currentTimeMillis();
while ((read = inputStream.read(buffer)) != -1) {
total += read;
outputStream.write(buffer);
}
System.out.println("发送总字节数: " + total + ", 耗时: " + (System.currentTimeMillis() - startTime));
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
outputStream.close();
inputStream.close();
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
3.8.3 mmap(Memory map)
mmap通过内存映射,将文件映射到内核缓冲区,同时,用户空间可以共享内核 空间的数据。这样,在进行网络传输时,就可以减少内核空间到用户控件的拷贝次数
public class NIOZeroCopyServer {
public static void main(String[] args) throws Exception{
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
serverSocketChannel.socket().bind(new InetSocketAddress("127.0.0.1",8090));
ByteBuffer buffer = ByteBuffer.allocate(8 * 1024);
while (true){
SocketChannel socketChannel = serverSocketChannel.accept();
int read = 0;
while ((read = socketChannel.read(buffer)) != -1){
buffer.rewind();
}
}
}
}
public class NIOZeroCopyClient {
public static void main(String[] args) throws Exception {
SocketChannel socketChannel = SocketChannel.open();
socketChannel.connect(new InetSocketAddress("127.0.0.1", 8090));
File file = new File("files//Typora.zip");
long fileSize = file.length();
System.out.println("文件大小 fileSize = " + fileSize);
FileInputStream inputStream = new FileInputStream(file);
FileChannel inputStreamChannel = inputStream.getChannel();
//在linux下一个transferTo 方法就可以完成传输
//在windows 下 一次调用 transferTo 只能发送 8m , 就需要分段传输文件, 而且要主要
//传输时的位置 =》 课后思考...
//transferTo 底层使用到零拷贝
long count = (long) ((fileSize % (8 * 1024 * 1024)) == 0 ? fileSize / (8 * 1024 * 1024) : fileSize / (8 * 1024 * 1024) + 1);
int position = 0;
long startTime = System.currentTimeMillis();
while (count-- > 0) {
long transferCount = inputStreamChannel.transferTo(position, 8 * 1024 * 1024, socketChannel);
System.out.println("发送的总的字节数 =" + transferCount + " 耗时:" + (System.currentTimeMillis() - startTime));
position += (8 * 1024 * 1024);
}
//关闭
inputStreamChannel.close();
}
}
3.8.4 snedFile
Linux 2.1 sendFile
Linux 2.1版本提供了 sendFile函数,其基本 原理如下:数据根本不 经过用户态,直接从内 核缓冲区进入到Socket Buffer,同时,由于和用 户态完全无关,就减少 了一次上下文切换
Linux 2.4 sendFile
Linux在2.4版本中,做了 一些修改,避免了从内核 缓冲区拷贝到Socket buffer的操作,直接拷贝到 协议栈,从而再一次减少 了数据拷贝
3.9 原生 NIO 时序图
server
client
4 AIO (NIO2.0)
4.1 介绍
http://www.52im.net/thread-306-1-1.html