java零拷贝

服务器有这样一个需求,读取一个文件,然后通过socket传输给客户端,代码如下
java零拷贝_第1张图片
这上面的代码看起来就是把文件读取到byte数组,然后在通过byte数组写给客户端。但其实经过了4次数据拷贝,三次内核系统切换。
java零拷贝_第2张图片
因为java是没办法直接读取文件的,需要通过先将文件读取到内核缓冲区,然后在读取到用户缓冲区,也就是byte数组,同样java没办法直接写数据,需要先将数据转换存入到socket缓冲区,然后再将数据拷贝到网卡进行数据的发送。所以经过了4次数据拷贝,三次内核系统切换。

NIO进行改进1:

我们可以将上面的过程改为 从FileChannel读,写入ByteBuffer,然后从ByteBuffer读,向SocketChannel写。

但普通的ByteBuffer还不行,需要使用ByteBuffer.allocateDirect(16) , 该ByteBuffer使用的是操作系统的内存,该内存操作系统可以直接操作,java也可以直接操作,相当于文件数据从磁盘拷贝到了这块内存,java有可以直接操作这块内存,就只是这一步省略了一次数据的拷贝
java零拷贝_第3张图片
进一步改进,(底层采用了 linux 2.1后提供的sendFile方法),在java中FileChannel中有transferTO/transferFrom方法拷贝数据

java零拷贝_第4张图片

  1. java调用transferTo方法后,要从java程序的用户态切换至内核态,使用DMA将数据读入内核缓冲区不会使用cpu
  2. 数据从内核缓冲区传输到socket缓冲区,cpu会参与拷贝
  3. 最后使用DMA将socket缓冲区的数据写入网卡,不会使用cpu

这样少了两次java和操作系统的切换

进一步优化,到了linux2.4,java方法还是没变
java零拷贝_第5张图片

  1. java调用transferTo方法后,要从java程序的用户态切换至内核态,使用DMA将数据读入内核缓冲区,不会使用cpu
  2. 只会将一些offset和length 信息拷入socket缓冲区,几乎无消耗
  3. 使用DMA将内核缓冲区的数据写入网卡,不会使用cpu

这种java到操作系统的切换只有1次,数据的拷贝只有两次

最后两种都可以叫做零拷贝。零拷贝指的是不用在java内存之间进行拷贝了。零拷贝的优点是:

  • 用户态和内核态的切换少
  • 不利用cpu计算,减少cpu缓存伪共享
  • 零拷贝适合小文件传输

你可能感兴趣的:(java笔记,java,服务器,开发语言)