深入分析通过Socket进行数据文件传递中的传统IO的弊端以及NIO的零拷贝实现原理,及用户空间和内核空间的切换方式

传统的IO流程

在这个过程中:

  1. 数据从磁盘拷贝进内核空间缓冲区
  2. 从内核空间缓冲区拷贝到用户空间缓冲区
  3. 从用户空间缓冲区拷贝回内核空间缓冲区
  4. 在从内核空间缓冲区拷贝到socket的缓冲区
  5. 由Socket缓存区传递给数据发送引擎发送

第三步的必要性:

IO操作涉及到本地方法,java担心,当使用native本地方法对堆内数组进行操作时发生GC, 因为堆内内存是受JVM影响的,一旦发生了垃圾回收机制就使得全部数据都是错乱的,而堆外内存是不受JVM控制的.

就这样, 前前后后一共发生了4次数据的拷贝,用户空间模式和内核空间模式来回切换了4次, 其中用户空间参与的第二次和第三次拷贝并没有对数据进行任何改动,它仅仅是起到了中转的作用; 这恰恰是传统的IO的局限性

NIO的零拷贝

在NIO的数据传递模型中可以看到,用户明显少了用户空间缓冲区缓存数据的步骤, 减少了两次不必要的数据的拷贝,以及不必要的上下文切换, 具体如下:

  1. 数据从磁盘写入内核空间缓冲区
  2. 再从内核空间缓冲区写入到Socket缓冲区
  3. 由Socket缓存区传递给数据发送引擎发送

然而这个模型中仍然有问题存在,在内核空间缓冲区中仍然存在数据的拷贝

  • 数据从内核空间缓冲区拷贝进了Socket缓冲区

这种现状也是有办法解决的

在2.X版本的linux中,NIO的零拷贝模型如下:

这个模型中充分利用了Scatter/Gather 分散和汇聚的特性

这张图是最完美的零拷贝模型,

  1. 首先文件从磁盘中加载进内核空间缓冲区
  2. CPU将内核空间缓冲区存储的数据的adress以及数据的大小存放进Socket
  3. 协议引擎根据socket提供的数据的描述,直接去内核缓冲区取出数据

第2步 一个完整的可用的buffer被分散在两个buffer中, 可以理解成是一个分散的过程 Scatter

第3步 操作系统去收集buffer,可以理解成一个Gather的过程

从而实现了真正的零拷贝

回到Java

除了上面的第一张图片以外,其他图片中数据全部在内核缓冲区,这部分空间对于人来说其实是一个黑盒,于是java提供了封装类帮我们和这块黑盒打交道

mappedByteBuffer



这是他的继承体系,和HeadByteBuffer位于同一级,我们称它为内存映射文件 他是通道的调用map()方法得来的, 这个mappedByteBuffer相对于普通的buffer而言,他并没有板板整整的维护自己的数组,相反直接关联着堆外内存,针对它的任何修改,操作系统都会自动的同步到文件中

如下修改内存buffer,却更新了文件

RandomAccessFile randomAccessFile = new RandomAccessFile("123.txt", "rw");  //class sun.nio.ch.FileChannelImpl
FileChannel channel = randomAccessFile.getChannel();
System.out.println(channel.getClass());
MappedByteBuffer mappedByteBuffer = channel.map(FileChannel.MapMode.READ_WRITE, 0, 5);
// todo 接下来我们直接修改内存中的内容就行了,不需要修改文件
mappedByteBuffer.put(0, (byte) 'a');
mappedByteBuffer.put(3, (byte) 'b');
randomAccessFile.close();
channel.close();

关于FileChannel.MapMode文件通道的映射模型 是个枚举:

  • PRIVATE
  • READ_WRITE
  • READ_ONLY

    当我们想构建read_write类型的只能使用 RandomAccessFile类型的文件stream, 通过它的rw参数,设置为可读写的类型

关于ByteBufferByteBuffer.allocateDirect()

public static ByteBuffer allocateDirect(int capacity) {
return new DirectByteBuffer(capacity);
} class DirectByteBuffer extends MappedByteBuffer implements DirectBuffer
{
...
}

最常用的ByteBuffer的allocateDirect()底层使用同样是MappedBytebuffer的实现类,DirectByteBuffer,这个对象相对于HeapByteBuffer来说,他并没有初始化父类ByteBuffer中的数组,但是它使用了超类BUffer中的Long类型的adress关键字

adress关键字的作用是 存放了一个堆外的地址,这个地址标记着一个堆外数组的位置,使得java可以使用unsafe类下的本地方法,操作adress标记的堆外内存,这样就省去了在第一张图片中的还要把堆内数组拷贝到堆外再进行读写的弊端,实现了零拷贝

scattering 和 gathering在NIO编程中的体现

scattering是一个分散的过程,即把一整块数据分散在不同的buffer中,而gathering与之相反,是一个聚集的过程,只有搜集全所有的全部的buffer得到的数据才是有意义的

例子: 自定义网络协议 将请求头分装成多个缓存buffer中,实现了天然的解析

ByteBuffer[] byteBuffers = new ByteBuffer[3];
byteBuffers[0] = ByteBuffer.allocate(2);
byteBuffers[1] = ByteBuffer.allocate(3);
byteBuffers[2] = ByteBuffer.allocate(4); SocketChannel client = serverSocketChannel.accept();
long read = client.read(byteBuffers);

NIO零拷贝的深入分析的更多相关文章

  1. NIO学习笔记,从Linux IO演化模型到Netty—— Java NIO零拷贝

    同样只是大致上的认识. 其中,当使用transferFrom,transferTo的时候用的sendfile(). 如果系统内核不支持 sendfile,进一步执行 transferToTrusted ...

  2. Java零拷贝

    1.摘要 零拷贝的“零”是指用户态和内核态间copy数据的次数为零. 传统的数据copy(文件到文件.client到server等)涉及到四次用户态内核态切换.四次copy.四次copy中,两次在用户 ...

  3. 深入剖析Linux IO原理和几种零拷贝机制的实现

    深入剖析Linux IO原理和几种零拷贝机制的实现 来源 https://zhuanlan.zhihu.com/p/83398714 零壹技术栈      公众号[零壹技术栈] 前言 零拷贝(Zero ...

  4. 【Netty技术专题】「原理分析系列」Netty强大特性之ByteBuf零拷贝技术原理分析

    零拷贝Zero-Copy 我们先来看下它的定义: "Zero-copy" describes computer operations in which the CPU does n ...

  5. Netty 零拷贝(一)NIO 对零拷贝的支持

    Netty 零拷贝(二)NIO 对零拷贝的支持 Netty 系列目录 (https://www.cnblogs.com/binarylei/p/10117436.html) 非直接缓冲区(HeapBy ...

  6. 零拷贝详解 Java NIO学习笔记四(零拷贝详解)

    转 https://blog.csdn.net/u013096088/article/details/79122671 Java NIO学习笔记四(零拷贝详解) 2018年01月21日 20:20:5 ...

  7. NIO学习笔记,从Linux IO演化模型到Netty—— Linux零拷贝

    这里只是感性地认识Linux零拷贝,不涉及具体细节. 1.Linux传统的数据拷贝 用户进程是不能直接访问文件系统的,要先切换到内核态,发起系统调用,DMA把磁盘中的数据写入内核空间,内核再把数据拷贝 ...

  8. NIO 与 零拷贝

    零拷贝介绍 零拷贝是网络编程的关键, 很多性能优化都需要零拷贝. 在 Java程序中, 常用的零拷贝方式有m(memory)map[内存映射] 和 sendFile.它们在OS中又是怎样的设计? NI ...

  9. Linux、JDK、Netty中的NIO与零拷贝

    一.先理解内核空间与用户空间 Linux 按照特权等级,把进程的运行空间分为内核空间和用户空间,分别对应着下图中, CPU 特权等级分为4个,Linux 使用 Ring 0 和 Ring 3. 内核空 ...

随机推荐

  1. Appium移动端自动化测试--控件定位方法

    常用定位手段 id Accessibility ID XPath 控件基础知识 DOM: Document Object Model文档对象模型 DOM应用:最早应用于HTML和Javascript的 ...

  2. LiteByte教程

    转载请标明原文地址:https://www.cnblogs.com/zhangyukof/p/12073041.html 简介 LiteByte是一种轻量级的二进制数据交换格式.体积小巧.简单易用是设 ...

  3. webpack学习_webpack-dev-server自动编译代码

    之前每次修改完之后都要执行npm run build来编译,下面有三种方式可以实现代码变化后自动编译代码,下面只重点说webpack-dev-server,其他的请看webpack开发文档 1.web ...

  4. 使用node+express+mongodb实现用户注册、登录和验证功能

    无论是手机端还是pc端,几乎都包含登录注册方面功能,今天就使用node+express+mongodb实现一套登录注册功能,这里需要自己去安装MongoDB环境,如果没有安装可以看这篇关于MongoD ...

  5. python GUI编程tkinter示例之目录树遍历工具

    摘录 python核心编程 本节我们将展示一个中级的tkinter应用实例,这个应用是一个目录树遍历工具:它会从当前目录开始,提供一个文件列表,双击列表中任意的其他目录,就会使得工具切换到新目录中,用 ...

  6. C#程序编写高质量代码改善的157个建议【10-12】[创建对象时需要考虑是否实现比较器、区别对待==和Equals]

    前言 建议10.创建对象时需要考虑是否实现比较器 建议11.区别对待==和Equals 建议12.重写Equals时也要重写GetHashCode 建议10.创建对象时需要考虑是否实现比较器 有对象的 ...

  7. Java面试题_第二阶段(Servlet、HTTP、Session、JSP、 Ajax、Filter、JDBC、Mysql、Spring)

    1.1. 描述Servlet调用过程? 答案: (1)在浏览器输入地址,浏览器先去查找hosts文件,将主机名翻译为ip地址,如果找不到就再去查询dns服务器将主机名翻译成ip地址. (2)浏览器根据 ...

  8. MySql事务的简单使用

    4个特性 原子性:一个事务中的所有操作,要么全部完成,要么全部不完成,不会结束在中间某个环节.事务在执行过程中发生错误,会被回滚(rollback)到事务开始前的状态 一致性:在事务开始前和事务结束以 ...

  9. 如何从Mac删除恶意广告软件,摆脱那些通过弹出广告或工具栏入侵Mac的恶意软件

    厌倦了那些利用弹出式广告和工具栏之类入侵Mac的恶意软件?该如何摆脱Mac上的恶意软件呢?今天小编为大家带来两种方法从Mac 删除广告软件,甚至阻止它到达您的Mac,感兴趣的朋友一起来看看吧! 方法一 ...

  10. 面试连环炮系列(十一):说说你们的分布式ID设计方案

    说说你们的分布式ID设计方案 我们采用Snowflake算法,生成一个64bit的数字,64bit被划分成多个段,分别表示时间戳.机器编码.序号. 41位的时间序列(精确到毫秒,41位的长度可以使用6 ...