NIO之零拷贝

1. 传统IO

1.1 工作流程

File f = new File("helloword/data.txt");
RandomAccessFile file = new RandomAccessFile(file, "r");

byte[] buf = new byte[(int)f.length()];
file.read(buf);

Socket socket = ...;
socket.getOutputStream().write(buf);

  1.  调用read方法后,Java程序由用户态切换为内核态
  2. 在内核态调用操作系统的读能力,将数据读入内核缓冲区,这个期间用户线程阻塞,操作系统使用DMA来实现文件读。
  3. 从内核态切回用户态,将数据从内核缓冲区拷贝到用户缓冲区,这期间CPU会参与拷贝
  4. 调用write方法,这时又发生从用户态切换到内核态,会将数据从用户缓冲区拷贝到socket缓冲区,CPU参与拷贝
  5. 最后向网卡写入数据,调用操作系统的写能力,使用DMA将socket缓冲区的数据写入网卡,不会使用CPU
  • 经过三次模式切换
  • 经过四次数据拷贝

2. NIO优化一

ByteBuffer buffer = ByteBuffer.allocateDirect(10);

  • 用户态缓冲区逻辑地址内核态缓冲区逻辑地址映射到同一物理地址上。
  • Java可以通过DirectByteBuffer这个引用访问到堆外用户缓冲区
    • 这块内存不受GC的影响,因此内存地址固定,有助于IO读写
    • Java中DirectByteBuffer对象仅维护此内存地址的虚引用,该内存回收分为两步
      • DirectByteBuffer对象被垃圾回收,将虚引用加入引用队列
      • 通过专门的线程访问引用队列,根据虚引用释放堆外内存。
  • 总结
    • 模式切换次数不变
    • 但是会少了1次数据拷贝

3. NIO优化二

channel.transferTo();
channel.transferFrom();

  1. Java调用transferTo 方法后,Java程序从用户态切换到内核态,使用DMA将数据直接读入内核缓冲区,不会使用CPU
  2. 数据从内核缓冲区传输到socket缓冲区,CPU参与拷贝
  3. 最后使用DMA将socket缓冲区的数据写入网卡,也不会使用CPU
  •  底层使用了Linux2.1 提供的sendFile方法
  • 只发生了一次模式切换
  • 数据拷贝还是三次。

4. NIO优化三

channel.transferTo();
channel.transferFrom();

  1.  Java调用transforTo方法后,Java程序从用户态切换为内核态,使用DMA将数据读入内核缓冲区,不会使用CPU
  2. 只会将一些offset和length信息拷入socket缓冲区,几乎无消耗
  3. 使用DMA将内核缓冲区的数据写入网卡,不会使用CPU
  • 底层使用了linux2.4提供的新技术
  • 还是一次模式切换
  • 但是数据拷贝变为两次。

5. 什么是零拷贝

  • 零拷贝不是无拷贝,而是不会拷贝数据到JVM内存中。
  • 零拷贝的优点
    • 更少的用户态和内核态的切换
    • 不利用CPU计算,减少CPU缓存伪共享
    • 零拷贝适合小文件传输。

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值