zoukankan      html  css  js  c++  java
  • Java IO学习笔记一:为什么带Buffer的比不带Buffer的快

    作者:Grey

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

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

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

    其中R大的一个回答:

    image

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

    环境:CentOS 7, jdk1.8

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

    static byte[] data = "123456789
    ".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
    ".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
    ", 10)             = 10
    write(4, "123456789
    ", 10)             = 10
    write(4, "123456789
    ", 10)             = 10
    write(4, "123456789
    ", 10)             = 10
    write(4, "123456789
    ", 10)             = 10
    write(4, "123456789
    ", 10)             = 10
    write(4, "123456789
    ", 10)             = 10
    write(4, "123456789
    ", 10)             = 10
    write(4, "123456789
    ", 10)             = 10
    write(4, "123456789
    ", 10)             = 10
    write(4, "123456789
    ", 10)             = 10
    write(4, "123456789
    ", 10)             = 10
    write(4, "123456789
    ", 10)             = 10
    write(4, "123456789
    ", 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
    123456789
    123456789
    12"..., 8190) = 8190
    write(4, "123456789
    123456789
    123456789
    12"..., 8190) = 8190
    write(4, "123456789
    123456789
    123456789
    12"..., 8190) = 8190
    write(4, "123456789
    123456789
    123456789
    12"..., 8190) = 8190
    write(4, "123456789
    123456789
    123456789
    12"..., 8190) = 8190
    write(4, "123456789
    123456789
    123456789
    12"..., 8190) = 8190
    
    

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

    源码:Github

  • 相关阅读:
    addslashes与mysql_real_escape_string的区别
    gcc
    php函数
    JAVA基本类库介绍
    Linux系统精华教程
    CJIAN
    我的未来不是梦! 全新时代
    html标签属性disabled用法 全新时代
    javamail收取邮件demo code 全新时代
    TortoiseSVN安装注意事项及中文语言包安装 全新时代
  • 原文地址:https://www.cnblogs.com/greyzeng/p/14866429.html
Copyright © 2011-2022 走看看