作者:Grey

原文地址:Java IO学习笔记一:为什么带Buffer的比不带Buffer的快

Java中为什么BufferedReader,BufferedWriter要比FileReader 和 FileWriter高效?

问题来自于:https://www.zhihu.com/question/29351698

其中R大的一个回答:

现在我们可以通过实验来说明这个问题:

环境:CentOS 7, jdk1.8

首先,写一个不带buffer的代码

static byte[] data = "123456789\n".getBytes();
static String path = "/data/io/out.txt";
public static void testBasicFileIO() throws Exception {
File file = new File(path);
FileOutputStream out = new FileOutputStream(file);
while (true) {
out.write(data);
}
}

同时,我们写一个带buffer的代码

public static void testBufferedFileIO() throws Exception {
File file = new File(path);
BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(file));
while (true) {
out.write(data);
}
}

通过main函数的args参数来控制执行哪个方法,完整代码为:

import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream; public class OSFileIO { static byte[] data = "123456789\n".getBytes();
static String path = "/data/io/out.txt"; public static void main(String[] args) throws Exception {
switch (args[0]) {
case "0":
testBasicFileIO();
break;
case "1":
testBufferedFileIO();
break;
default:
break;
}
} public static void testBasicFileIO() throws Exception {
File file = new File(path);
FileOutputStream out = new FileOutputStream(file);
while (true) {
out.write(data);
}
} public static void testBufferedFileIO() throws Exception {
File file = new File(path);
BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(file));
while (true) {
out.write(data);
}
}
}

在Linux(先安装好jdk1.8)中,准备好目录:

mkdir -p /data/io

安装必要工具

yum install -y strace lsof  pmap tcpdump

将OSFileIO.java这个类上传到/data/io目录下,在/data/io目录下,新建一个mysh.sh的脚本,脚本内容如下:

rm -rf *out*
/usr/local/jdk/bin/javac OSFileIO.java
strace -ff -o out /usr/local/jdk/bin/java OSFileIO $1

赋予mysh.sh执行权限

chmod +x /data/io/mysh.sh

先监控带buffer的writer和不带buffer的writer的写效率,

不带buffer的writer效率,在控制台执行:

./mysh.sh 0

打开另外一个控制台,进入/data/io目录,监控生成out文件大小的速度,不断执行

ll -h

可以看到out.txt的增长速度

-rw-r--r--. 1 root root 2.1M Jun 10 19:50 out.txt

...

-rw-r--r--. 1 root root 5.3M Jun 10 19:51 out.txt

重新执行,使用带buffer的writer

./mysh.sh 1

在另外一个控制台,进入/data/io目录,继续监控out.txt的增长

cd /data/io
ll -h

可以看到out.txt的增长速度明显更快

-rw-r--r--. 1 root root 290M Jun 10 19:54 out.txt

....

-rw-r--r--. 1 root root 768M Jun 10 19:54 out.txt

....

-rw-r--r--. 1 root root 1.4G Jun 10 19:55 out.txt

这个是表现,我们再观察一下使用buffer和未使用buffer的writer在执行的时候,系统调用的次数。

重新执行

./mysh.sh 0

执行大约10秒后,停止执行

由于mysh.sh中使用了strace, 可以用于跟踪和分析进程执行时中系统调用和耗时以及占用cpu的比例

查看生成的out文件列表:

[root@io io]# ll
total 60708
-rwxr-xr-x. 1 root root 106 Jun 10 19:25 mysh.sh
-rw-r--r--. 1 root root 3981 Jun 10 20:08 OSFileIO.class
-rw-r--r--. 1 root root 4587 Jun 10 19:29 OSFileIO.java
-rw-r--r--. 1 root root 9379 Jun 10 20:10 out.6916
-rw-r--r--. 1 root root 50363725 Jun 10 20:10 out.6917
-rw-r--r--. 1 root root 1027 Jun 10 20:10 out.6918
-rw-r--r--. 1 root root 885 Jun 10 20:10 out.6919
-rw-r--r--. 1 root root 850 Jun 10 20:10 out.6920
-rw-r--r--. 1 root root 948 Jun 10 20:10 out.6921
-rw-r--r--. 1 root root 885 Jun 10 20:10 out.6922
-rw-r--r--. 1 root root 885 Jun 10 20:10 out.6923
-rw-r--r--. 1 root root 850 Jun 10 20:10 out.6924
-rw-r--r--. 1 root root 1134 Jun 10 20:10 out.6925
-rw-r--r--. 1 root root 26835 Jun 10 20:10 out.6926
-rw-r--r--. 1 root root 1343 Jun 10 20:10 out.6927
-rw-r--r--. 1 root root 1210 Jun 10 20:10 out.6928
-rw-r--r--. 1 root root 2324 Jun 10 20:10 out.6929
-rw-r--r--. 1 root root 9954 Jun 10 20:10 out.6930
-rw-r--r--. 1 root root 9792 Jun 10 20:10 out.6931
-rw-r--r--. 1 root root 9477 Jun 10 20:10 out.6932
-rw-r--r--. 1 root root 8295 Jun 10 20:10 out.6933
-rw-r--r--. 1 root root 1190 Jun 10 20:10 out.6934
-rw-r--r--. 1 root root 485668 Jun 10 20:10 out.6935
-rw-r--r--. 1 root root 2008 Jun 10 20:10 out.7023
-rw-r--r--. 1 root root 11152490 Jun 10 20:10 out.txt

可以看到

-rw-r--r--. 1 root root 50363725 Jun 10 20:10 out.6917

是主线程生成的系统调用,查看这个文件,可以看到,系统调用write的次数很多

write(4, "123456789\n", 10)             = 10
write(4, "123456789\n", 10) = 10
write(4, "123456789\n", 10) = 10
write(4, "123456789\n", 10) = 10
write(4, "123456789\n", 10) = 10
write(4, "123456789\n", 10) = 10
write(4, "123456789\n", 10) = 10
write(4, "123456789\n", 10) = 10
write(4, "123456789\n", 10) = 10
write(4, "123456789\n", 10) = 10
write(4, "123456789\n", 10) = 10
write(4, "123456789\n", 10) = 10
write(4, "123456789\n", 10) = 10
write(4, "123456789\n", 10) = 10

切换成带buffer的执行,大约执行10秒

./mysh.sh 1

同样可以通过ll查看

[root@io io]# ll
total 388808
-rwxr-xr-x. 1 root root 106 Jun 10 19:25 mysh.sh
-rw-r--r--. 1 root root 3981 Jun 10 20:17 OSFileIO.class
-rw-r--r--. 1 root root 4587 Jun 10 19:29 OSFileIO.java
-rw-r--r--. 1 root root 9526 Jun 10 20:18 out.7053
-rw-r--r--. 1 root root 3262847 Jun 10 20:18 out.7054
-rw-r--r--. 1 root root 1076 Jun 10 20:18 out.7055
-rw-r--r--. 1 root root 885 Jun 10 20:18 out.7056
-rw-r--r--. 1 root root 885 Jun 10 20:18 out.7057
-rw-r--r--. 1 root root 948 Jun 10 20:18 out.7058
-rw-r--r--. 1 root root 983 Jun 10 20:18 out.7059
-rw-r--r--. 1 root root 948 Jun 10 20:18 out.7060
-rw-r--r--. 1 root root 885 Jun 10 20:18 out.7061
-rw-r--r--. 1 root root 1099 Jun 10 20:18 out.7062
-rw-r--r--. 1 root root 3812 Jun 10 20:18 out.7063
-rw-r--r--. 1 root root 1259 Jun 10 20:18 out.7064
-rw-r--r--. 1 root root 1245 Jun 10 20:18 out.7065
-rw-r--r--. 1 root root 2337 Jun 10 20:18 out.7066
-rw-r--r--. 1 root root 6415 Jun 10 20:18 out.7067
-rw-r--r--. 1 root root 5486 Jun 10 20:18 out.7068
-rw-r--r--. 1 root root 6347 Jun 10 20:18 out.7069
-rw-r--r--. 1 root root 4972 Jun 10 20:18 out.7070
-rw-r--r--. 1 root root 1008 Jun 10 20:18 out.7071
-rw-r--r--. 1 root root 25438 Jun 10 20:18 out.7072
-rw-r--r--. 1 root root 2071 Jun 10 20:18 out.7073
-rw-r--r--. 1 root root 394725240 Jun 10 20:18 out.txt

其中

-rw-r--r--. 1 root root   3262847 Jun 10 20:18 out.7054

为主线程的系统调用,打开这个文件可以看到

write(4, "123456789\n123456789\n123456789\n12"..., 8190) = 8190
write(4, "123456789\n123456789\n123456789\n12"..., 8190) = 8190
write(4, "123456789\n123456789\n123456789\n12"..., 8190) = 8190
write(4, "123456789\n123456789\n123456789\n12"..., 8190) = 8190
write(4, "123456789\n123456789\n123456789\n12"..., 8190) = 8190
write(4, "123456789\n123456789\n123456789\n12"..., 8190) = 8190

不是每次写都调用系统的write,而是凑齐8190后再调用一次系统的write,所以速度更快。

源码:Github

Java IO学习笔记一:为什么带Buffer的比不带Buffer的快的更多相关文章

  1. Java IO学习笔记:概念与原理

    Java IO学习笔记:概念与原理   一.概念   Java中对文件的操作是以流的方式进行的.流是Java内存中的一组有序数据序列.Java将数据从源(文件.内存.键盘.网络)读入到内存 中,形成了 ...

  2. Java IO学习笔记总结

    Java IO学习笔记总结 前言 前面的八篇文章详细的讲述了Java IO的操作方法,文章列表如下 基本的文件操作 字符流和字节流的操作 InputStreamReader和OutputStreamW ...

  3. Java IO学习笔记三

    Java IO学习笔记三 在整个IO包中,实际上就是分为字节流和字符流,但是除了这两个流之外,还存在了一组字节流-字符流的转换类. OutputStreamWriter:是Writer的子类,将输出的 ...

  4. Java IO学习笔记二

    Java IO学习笔记二 流的概念 在程序中所有的数据都是以流的方式进行传输或保存的,程序需要数据的时候要使用输入流读取数据,而当程序需要将一些数据保存起来的时候,就要使用输出流完成. 程序中的输入输 ...

  5. Java IO学习笔记一

    Java IO学习笔记一 File File是文件和目录路径名的抽象表示形式,总的来说就是java创建删除文件目录的一个类库,但是作用不仅仅于此,详细见官方文档 构造函数 File(File pare ...

  6. Java IO学习笔记八

    BufferedReader和BufferedWriter 这两个类是高效率的提高文件的读取速度,它们为字符输入和输出提供了一个缓冲区,可以显著的调高写入和读取的速度,特别针对大量的磁盘文件读取的时候 ...

  7. Java IO学习笔记六

    打印流 在整个IO包中,打印流是输出信息最方便的类,主要包含字节打印流(PrintStream)和字符打印流(PrintWrite).打印流提供了非常方便的打印功能,可以打印任何的数据类型,例如:小数 ...

  8. Java IO学习笔记(五)对象流

    1.Object流:直接将Object写入或读出. 2.序列化:将Object直接转化成字节流写到硬盘或网络上. 3.如果想把一个对象转化成字节流,该对象的实体类必须得实现Serializable接口 ...

  9. Java IO学习笔记(四)打印流

    1.只有输出流才有打印流:PrintWriter和PrintStream分别针对字符和字节,提供了重载的print,Println方法用于多种数据类型的输出.PrintWriter和PrintStre ...

  10. Java IO学习笔记(三)转换流、数据流、字节数组流

    转换流 1.转换流:将字节流转换成字符流,转换之后就可以一个字符一个字符的往程序写内容了,并且可以调用字符节点流的write(String s)方法,还可以在外面套用BufferedReader()和 ...

随机推荐

  1. JavaScript----函数的封装、继承和多态

    1.封装:把实现一个功能的代码放在一个函数中封装起来,以后再想实现这个功能的时候,我们不需要重新的编写代码了,只需要执行对应的函数即可,我们把这种机制就称之为"函数的封装"--&g ...

  2. JSTL的全称:JSP Standard Tag Library, jsp 标准标签库

    JSTL的全称:JSP Standard Tag Library, jsp 标准标签库 JSTL的作用     提供给Java web开发人员一个标准通过的标签函数库和EL来取代传统直接在页面上嵌入j ...

  3. spring mvc DispatcherServlet详解之一---处理请求深入解析

    要深入理解spring mvc的工作流程,就需要先了解spring mvc的架构: 从上图可以看到 前端控制器DispatcherServlet在其中起着主导作用,理解了DispatcherServl ...

  4. sql 成绩表 case then

    select * from  dbo.tb_Scroe select Name,(select count(*) from tb_Scroe where Name = t.Name and Scroe ...

  5. C# - 重定义一个接口的实现

    using System;using System.Collections.Generic;using System.Text; namespace MyTester{    public inter ...

  6. 金蝶K3外购入库单单价取数规则调整

    涉及界面: 问题:财务抱怨外购入库单价格取错,单价多除了一次税率 例如,采购单里面注明了价格是不含税15.3256 结果在外购入库单里面,又自做主张除以税率17%,把采购成本搞成了13.0988, 咨 ...

  7. python zeros用法实例

    编程就是踩坑的过程.今天又踩了一个坑,做个积累吧. 在给数组赋初始值的时候,经常会用到0数组,而Python中,我们使用zero()函数来实现.在默认的情况下,zeros创建的数组元素类型是浮点型的, ...

  8. 【转】求职面试-HR会问你什么问题?

    前言 面试是程序员们经常探讨的话题,只要你通过前面的技术面,最后一面必然是HR面试,基本上到了这关你离Offer的距离应该不会太远了,但有的公司的HR是有刷入的权利,如果你并不能很好的应对HR的问题, ...

  9. 证券化代币的时代已经到来,STO将引爆区块链经济

    STOs 似乎会在 2019 年取代 ICOs,即使不是完全取代,但置换的比例也会相当大.所有在美上市的公司都将按照 SEC 制定的相关规定进行交易.Vellum Capital 的 CEO 兼管理合 ...

  10. linux 切割文件的命令

    Head -1000 access.2016.log >> 10000_access.log