Java中IO流利用缓冲区的发展过程

在讲缓冲区这个概念前,我们先来了解什么是流。Java中的流按照不同的分类有很多,比如按照流的流向分,可以分为输入流和输出流;按照操作单元划分,可以划分为字节流和字符流;按照流的角色划分为节点流和处理流。

Java Io流共涉及40多个类,这些类看上去很杂乱,但实际上很有规则,而且彼此之间存在非常紧密的联系, Java I0流的40多个类都是从如下4个抽象类基类中派生出来的。

  • InputStream/Reader: 所有的输入流的基类,前者是字节输入流,后者是字符输入流。
  • OutputStream/Writer: 所有输出流的基类,前者是字节输出流,后者是字符输出流。

按操作方式分类结构图:
Java中IO流利用缓冲区的发展过程_第1张图片图片来源:Java Guide

按操作对象分类结构图:
Java中IO流利用缓冲区的发展过程_第2张图片
图片来源:Java Guide

通过上述的了解,大家对流有了一个大概的认知了。

流是什么?流是一个 过程 ,一个 动态 的概念。 既然有中间过程,那么就会有起点和终点。比如输入流:起点可以是存储介质,或者内存数组,终点是应用程序
早期版本的JDK是这样实现的,比如我们有一个将文件读取到另外一台服务器的操作。
比如采用FileInputStream
Java中IO流利用缓冲区的发展过程_第3张图片
采用这种方式,每次读取文件都是一次即时操作,多次读取则会造成多次的磁盘IO。
于是Java就提供了一个缓冲相关的类——BufferedInputStream,BufferedInputStream内部含有一个byte数组用作缓冲,可以一次性读取大量数据。减少磁盘IO次数。
Java中IO流利用缓冲区的发展过程_第4张图片
这样的话确实有效减少了磁盘的IO操作,提高了读取效率。但是还是有个问题,上述的过程中其实发生了四次数据拷贝,四次上下文切换。上述其实是BIO的做法(IO操作会堵塞),不利于充分使用CPU和IO。

为了减少这种数据拷贝和上下文切换,于是有了NIO,提供了FileChannel和ByteBuffer,这一部分内存是在堆外分配的,而且FileChannel的transferTo() 方法支持将数据从文件通道传输到给定的可写字节通道。上述的操作过程变成这样。
Java中IO流利用缓冲区的发展过程_第5张图片
这一部分内存是分配在堆外,Java称之为直接内存,这样上下文切换就减少到了两次,而且数据复制也减少到了三次(其中DMA copy 2次,CPU copy 1次)。

在 Linux 内核 2.4 及后期版本中,针对套接字缓冲区描述符做了相应调整,DMA自带了收集功能,对于用户方面,用法还是一样的,但是内部操作已经发生了改变:
Java中IO流利用缓冲区的发展过程_第6张图片

  • 第一步,transferTo() 方法引发 DMA 将文件内容拷贝到内核读取缓冲区。
  • 第二步,把包含数据位置和长度信息的描述符追加到套接字缓冲区,避免了内容整体的拷贝,DMA 引擎直接把数据从内核缓冲区传到协议引擎,从而消除了最后一次 CPU参与的拷贝动作。
    CPU参与的拷贝减少到了0次,上下文切换了两次。

你可能感兴趣的:(Java,#,Java核心)