日常开发下的零拷贝?

零拷贝:所谓零拷贝,不是说不拷贝(数据从硬盘到内核态的DMA拷贝是一定会有的),而是说没有CPU拷贝。网络编程的关键,很多性能优化都离不开零拷贝

传统IO数据拷贝过程:

过程说明

  • 在读取数据的时候数据一开始从硬盘经过DMA copy到内核态中,

  • 内核态再经过CPU copy到达user buffer(即用户态)

  • 再冲用户态经过CPU copy到socket buffer

  • 后再经过DMA copy到达协议栈(协议栈此处可自行查阅资料)

 

常见的零拷贝技术

    JAVA中的mmap(内存映射)和sendFile

mmap:指通过内存映射,将文件映射到内核缓冲区,以此用户空间可以共享内核空间的数据,这样在网络传输时,减少内核空间到用户空间的拷贝次数 

优点:减少了拷贝次数一次,但是状态切换还是3次

 

sendFile:Linux2.1版本优化中指的是sendFile函数

核心:数据不经过用户态,直接从内核缓冲区进入socket缓冲区,同时,由于与用户态无关,减少了一次上下文切换

优点:拷贝次数由4次转换为3,状态切换由3次转换为2次

说明:Linux2.4版本中做了一些优化,避免了从内核缓冲区拷贝到Socket Buffer的操作,数据从内核态直接拷贝到协议栈,实现零拷贝(其实不可说是完全的零拷贝,因为少部分数据【如数据头,长度等】其实还是由从内核态拷贝到socket Buffer)

 

案例分析

传统IO server

public class OldIoServer {
​
    public static void main(String[] args) throws Exception {
        ServerSocket serverSocket = new ServerSocket(7001);
​
        while (true){
            Socket accept = serverSocket.accept();
            DataInputStream dataInputStream = new DataInputStream(accept.getInputStream());
            try {
                byte[] bytes = new byte[4096];
                while (true) {
                    int readCount = dataInputStream.read(bytes);
                    if( -1 == readCount){
                        break;
                    }
                }
            }catch (Exception e){
                e.printStackTrace();
            }
        }
    }
}

传统IO client

public class OldIoClient {
​
    public static void main(String[] args) throws Exception {
        Socket socket = new Socket("localhost", 7001);
​
        String fileName = "xxxxxx.zip";
        InputStream inputStream = new FileInputStream(fileName);
        DataOutputStream dataOutputStream = new DataOutputStream(socket.getOutputStream());
​
        byte[] buffer = new byte[4096];
        long readCount;
        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));
​
        dataOutputStream.close();
        socket.close();
        inputStream.close();
    }
}

NIO方式:使用transferTo()方法

server

public class NewIOServer {
​
    public static void main(String[] args) throws IOException {
        InetSocketAddress address = new InetSocketAddress(7001);
        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
        ServerSocket serverSocket = serverSocketChannel.socket();
        serverSocket.bind(address);
​
        ByteBuffer byteBuffer = ByteBuffer.allocate(4096);
        while (true){
            SocketChannel socketChannel = serverSocketChannel.accept();
            int readCount = 0;
            while(-1 != readCount){
                try {
                    readCount = socketChannel.read(byteBuffer);
                }catch (Exception e){
//                    e.printStackTrace();
                    // 发生异常后直接break而不打印异常栈,则不会导致服务器不可用
                    break;
                }
​
                byteBuffer.rewind();
            }
        }
    }
}

NIO client

public class NewIOClient {
​
    public static void main(String[] args) throws IOException {
        SocketChannel socketChannel = SocketChannel.open();
        socketChannel.connect(new InetSocketAddress("localhost", 7001));
​
        String fileName = "xxxxxx.zip";
        FileChannel fileChannel = new FileInputStream(fileName).getChannel();
​
        long start = System.currentTimeMillis();
​
        //在linux下一个transferTO方法就可以完成传输,而windows下一次只能8M,所以只能分段传输
        //所以需要设置传输的位置
        long transferCount = fileChannel.transferTo(0, fileChannel.size(), socketChannel);
​
        long end = System.currentTimeMillis();
        System.out.println("transferCount = " + transferCount);
​
        System.out.println("传输整体耗时:"+ (end - start));
    }
}

​​​​​​​​​​​​​​验证说明

通过比较传统IO传输数据的耗时与使用NIO的方式传输数据的耗时,来说明零拷贝对网络传输的作用

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值