`

Java I/O关于缓冲区部分提高性能的源码分析【Stream】

阅读更多
拿FileInputStream来举例:
class FileInputStream extends InputStream


从顶级的InputStream开始

InputStream 定义了3个read方法。
read();
read(byte[]);
read(byte[],int off,int len);

第二个read(byte[])其实就是read(b, 0, b.length) ,所以等同于第三个;

第一个read()方法,api介绍如下:
引用
从输入流中读取数据的下一个字节。返回 0 到 255 范围内的 int 字节值。如果因为已经到达流末尾而没有可用的字节,则返回值 -1。在输入数据可用、检测到流末尾或者抛出异常前,此方法一直阻塞。
子类必须提供此方法的一个实现。


第三个read()方法,api介绍如下:
引用
将输入流中最多 len 个数据字节读入 byte 数组。尝试读取 len 个字节,但读取的字节也可能小于该值。以整数形式返回实际读取的字节数。
在输入数据可用、检测到流末尾或者抛出异常前,此方法一直阻塞。

如果 len 为 0,则不读取任何字节并返回 0;否则,尝试读取至少一个字节。如果因为流位于文件末尾而没有可用的字节,则返回值 -1;否则,至少读取一个字节并将其存储在 b 中。

将读取的第一个字节存储在元素 b[off] 中,下一个存储在 b[off+1] 中,依次类推。读取的字节数最多等于 len。设 k 为实际读取的字节数;这些字节将存储在 b[off] 到 b[off+k-1] 的元素中,不影响 b[off+k] 到 b[off+len-1] 的元素。

在任何情况下,b[0] 到 b[off] 的元素以及 b[off+len] 到 b[b.length-1] 的元素都不会受到影响。

类 InputStream 的 read(b, off, len) 方法重复调用方法 read()。如果第一次这样的调用导致 IOException,则从对 read(b, off, len) 方法的调用中返回该异常。如果对 read() 的任何后续调用导致 IOException,则捕获该异常并将其视为到达文件末尾;到达该点时读取的字节存储在 b 中,并返回发生异常之前读取的字节数。在已读取输入数据 len 的请求数量、检测到文件结束标记、抛出异常前,此方法的默认实现将一直阻塞。建议子类提供此方法更为有效的实现。


关于三段红字的注解:
第一段:InputStream是所有输入流的顶级类,当然只定义,不实现,具体的由子类去实现,如AudioInputStream, ByteArrayInputStream, FileInputStream等。
第二段:指明了InputStream的read(byte[],int off,int len)的实现方式,就是简单的调用read()方法而已,而read()方法是一次只读取一个字节,依然每次都要调用底层系统,所以InputStream的read(byte[],int off,int len)性能和直接调用read()一样,byte[]缓冲区在这是摆设。
第三段:正是由于第二段所说,才建议子类提供性能更好的方式来覆盖read(byte[],int off,int len)方法。

那InputStream的子类有哪些呢?看下API就知道了,这几只拿FileInputStream来说.
下面是FileInputStream的部分源码:
 public native int read() throws IOException;

 private native int readBytes(byte b[], int off, int len) throws IOException;

 public int read(byte b[]) throws IOException {
	return readBytes(b, 0, b.length);
    }

 public int read(byte b[], int off, int len) throws IOException {
	return readBytes(b, off, len);
    }

这里两个read()方法都是用本地方法实现,因为FileInputStream是跟底层的操作系统交互的,没有比用本地方法来实现的性能更好,更容易的了。所以这里就采用了第三段里的建议,真正实现了缓存的功能,虽然我们并不知道如何实现的。

那么既然FileInputStream已经实现了缓存来提高性能,那么BufferedInputStream又拿来干嘛?
先看api介绍:
引用
BufferedInputStream 为另一个输入流添加一些功能,即缓冲输入以及支持 mark 和 reset 方法的能力。在创建 BufferedInputStream 时,会创建一个内部缓冲区数组。在读取或跳过流中的字节时,可根据需要从包含的输入流再次填充该内部缓冲区,一次填充多个字节。mark 操作记录输入流中的某个点,reset 操作使得在从包含的输入流中获取新字节之前,再次读取自最后一次 mark 操作后读取的所有字节。


其实上面所说的“缓冲输入”并不是真正的像FileInputStream那样用本地方法来提高性能,而是指在这基础上,为了程序员操作方便,内部提供了一个缓冲区(byte[1024*8] buf),并装饰了FileInputStream类(构造BufferedInputStram时必须提供被装饰的InputStream就可看出)。
当用FileInputStream的时候,read()是从底层读一个字节,read(byte[],int off,int len)则是一次性读取了len-off个字节,我们需提供一个byte[]来存放,
而用BufferedInputStream的时候,其read()其实和read(byte[],int off,int len)一样,内部都是调用构造输入的FileInputStream的read(byte[],int off,int len)方法,将底层数据读入到byte[]里,而且byte[]不需要我们来提供,类本身定义了一个byte[] buf数组来存放这些数据,所以,如果使用BufferedInputStream我们的程序又不需要对byte[]数组操作的话,直接这样写就行了:
FileInputStream fis=new FileInputStream("d:\\a.txt");
BufferedInputStream bis=new BufferedInputStream(fis);
int data=0;
while((data=bis.read())!=-1){
	//......		
}

这样虽然也是一次读一个字节,但不是每次都从底层读取数据,而是一次调用底层系统读取了最多buf.length个字节到buf数组中,然后从buf中一次读一个字节,减少了频繁调用底层接口的开销。
等同于
FileInputStream fis=new FileInputStream("d:\\a.txt");
byte[] mybuff=new byte[1024];
int count=0;
while((count=fis.read(mybuff))!=-1){
     //......
}


如果是用BufferedInputStream的read(byte[],int off,int len)那缓冲区则由传入的byte[]来充当(虽然内部其实有时候还用到了buf,但表现出来的就是用传入的byte[]来缓冲)。

讲了这么多,那如果要缓冲那该用FileInputStream还是BufferedInputStream呢?回到上面紫色的文字,BufferedInputStream主要不是提供buf,而是封装了缓冲和标记/回读的功能。如果你既不用到标记/回读功能,又不要操作中间的缓冲数组,那显而易见直接用FileInputStream的read(byte[],int off,int len)是效率最高的。

最后说下为什么用缓冲性能就更好,因为应用程序可以将多个字节写入底层输出流中(native read(byte)),而不必针对每个字节写入都调用底层系统(native read())。OutputStream原理基本差不多,这里就不说了。

3
0
分享到:
评论

相关推荐

    芯片I/O缓冲及ESD电路设计

     针对引脚的输入输出缓冲(I/O buffer)电路设计,也可以称为输入输出接口(I/O interface)电路设计,是一颗完整芯片设计中不可或缺的组成部分,但是详细论述其设计规则的文章或者著作在国内却比较鲜见,这对初学...

    操作系统专科考试题

    简答题(共40分) 1、说明进程在三个基本状态...3、为什么说引入缓冲后可显著地提高I/O速度,6而使CPU与I/O 速度不匹配的矛盾得到缓和?(8分) 4、中断技术的基本原理是什么(8分) 5、文件的三种物理结构的特点(8分)

    Linux I/O 原理和 Zero-copy 技术全面揭秘

    两万字长文从虚拟内存、I/O 缓冲区,用户态&内核态以及 I/O 模式等等知识点全面而又详尽地剖析 Linux 系统的 I/O 底层原理,分析了 Linux 传统的 I/O 模式的弊端,进而引入 Linux Zero-copy 零拷贝技术的介绍和原理...

    利用缓冲区提高Java应用程序的IO性能

    利用缓冲区提高Java应用程序的IO性能

    浅析标准I/O缓冲区

     学习过编程的朋友都知道ANSI C里定义的标准I/O是一种带缓冲的磁盘I/O,目的是尽可能减少使用read和write系统调用的次数,从而提高I/O效率。标准I/O提供了3种类型的缓冲类型。  ● 全缓冲。在这种情况下,当填满...

    I/O缓冲池演示程序

    操作系统中的I/O管理,I/O缓冲池程序,使用C++编写,Windows环境

    Java I/O底层是如何工作的?

    本博文主要讨论I/O在底层是如何工作的。本文服务的读者,迫切希望了解Java I/O操作是在机器层面如何进行映射,以及应用运行时硬件都做了什么。...通常,进程执行操作系统的I/O请求包括数据从缓冲区排

    EDA/PLD中的浅析标准I/O缓冲区

     学习过编程的朋友都知道ANSI C里定义的标准I/O是一种带缓冲的高级磁盘I/O,目的是尽可能减少使用read和write系统调用的次数,从而提高I/O效率。标准I/O提供了3种类型的缓冲类型。  ● 全缓冲。在这种情况下,当...

    java生成线缓冲区的代码

    使用java写的线的缓冲区代码,使用eclipse直接打开就可以使用,这里返回的是经纬度的坐标串,修改传入坐标,直接可以得到线缓冲区的的坐标串,然后再可视化即可。

    java nio超好资源

    NIO 的创建目的是为了让 Java 程序员可以实现高速 I/O 而无需编写自定义的本机代码。NIO 将最耗时的 I/O 操作(即填充和提取缓冲区)转移回操作系统,因而可以极大地提高速度。

    SuperMap Objects Java线对象缓冲区分析

    本范例在Objects Java中示范经纬度坐标系下线对象的缓冲区创建,缓冲分析的左右缓冲半径单位为米,而且不相等,生成的缓冲区面数据集为经纬度坐标系。

    基于java NIO的简单聊天软件示例

    NIO是一种基于通道和缓冲区的I/O方式,它可以使用Native函数库直接分配堆外内存(区别于JVM的运行时数据区),然后通过一个存储在java堆里面的DirectByteBuffer对象作为这块内存的直接引用进行操作。这样能在一些...

    生产者与消费者问题 java

    生产者与消费者问题 java #include #include const unsigned short SIZE_OF_BUFFER = 10; //缓冲区长度 unsigned short ProductID = 0; //产品号 unsigned short ConsumeID = 0; //将被消耗的产品号 unsigned ...

    标准C的I/O库函数实现文件逆转

    属原创! 实现任意大小 使用任意缓冲区的文件逆转程序

    java源码包---java 源码 大量 实例

     Java数据压缩与传输实例,可以学习一下实例化套按字、得到文件输入流、压缩输入流、文件输出流、实例化缓冲区、写入数据到文件、关闭输入流、关闭套接字关闭输出流、输出错误信息等Java编程小技巧。 Java数组倒置...

    缓冲区溢出攻防的源码

    《缓冲区溢出攻防》的源码,很多人找我要,所以共享一下。只是雕虫小技,自己玩玩就行了,不要用于其它用途。 http://blog.csdn.net/LocalVar/archive/2004/05/29/3615025.aspx

    线缓冲区生成算法

    使用java代码编写的线缓冲区生成算法,下载工程,导入Eclipse直接可用,采用的缓冲区生成算法为平行双线法

    检查磁盘写速度工具(I/O速度)

    检查磁盘写速度工具 使用说明 filewritetest ./fwp ./ 10 8 fwp.exe <PATH> <FILE_NUM> (MB)> (KB)> [OPTION] -ff value(MB) flush data when amount of data has been written ...BUFF SIZE:缓冲大小,KB

    新输入输出(NIO)

    JDK 1.4 中引入的新输入输出 (NIO) 库在标准 Java 代码中提供了高速的、面向块的 I/O。本实用教程从高级概念到底层的编程细节,非常详细地介绍了 NIO 库。您将学到诸如缓冲区和通道这样的关键 I/O 元素的知识,并...

Global site tag (gtag.js) - Google Analytics