博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Java深入理解IO NIO在文件复制过程中的应用
阅读量:4107 次
发布时间:2019-05-25

本文共 9461 字,大约阅读时间需要 31 分钟。

 

第一种,传统的IO模式

private static void copyByIO(String srcPath, String dstPath) {        byte[] buffer = new byte[bufferSize];        FileInputStream fis = null;        FileOutputStream fos = null;        try {            fis = new FileInputStream(srcPath);            fos = new FileOutputStream(dstPath);            int len;            while ((len = fis.read(buffer)) != -1) {                fos.write(buffer, 0, len);            }        } catch (FileNotFoundException e) {            e.printStackTrace();        } catch (IOException e) {            e.printStackTrace();        } finally {            if (fis != null) {                try {                    fis.close();                } catch (IOException e) {                    e.printStackTrace();                }            }            if (fos != null) {                try {                    fos.close();                } catch (IOException e) {                    e.printStackTrace();                }            }        }    }

第二种,NIO的 directbuffer模式

private static void copyByNIO(String srcPath, String dstPath) {        FileInputStream fis = null;        FileOutputStream fos = null;        FileChannel fisChannel = null;        FileChannel fosChannel = null;        ByteBuffer buffer = ByteBuffer.allocateDirect(bufferSize);        try {            fis = new FileInputStream(srcPath);            fos = new FileOutputStream(dstPath);            fisChannel = fis.getChannel();            fosChannel = fos.getChannel();            while (fisChannel.read(buffer) != -1) {                buffer.flip();                fosChannel.write(buffer);                buffer.clear();            }        } catch (FileNotFoundException e) {            e.printStackTrace();        } catch (IOException e) {            e.printStackTrace();        } finally {            if (fisChannel != null) {                try {                    fisChannel.close();                } catch (IOException e) {                    e.printStackTrace();                }            }            if (fis != null) {                try {                    fis.close();                } catch (IOException e) {                    e.printStackTrace();                }            }            if (fosChannel != null) {                try {                    fosChannel.close();                } catch (IOException e) {                    e.printStackTrace();                }            }            if (fos != null) {                try {                    fos.close();                } catch (IOException e) {                    e.printStackTrace();                }            }        }    }

第3种,NIO transferto的模式

private static void copyByNIOTransfer(String srcPath, String dstPath) {        FileInputStream fis = null;        FileOutputStream fos = null;        FileChannel fisChannel = null;        FileChannel fosChannel = null;        try {            fis = new FileInputStream(srcPath);            fos = new FileOutputStream(dstPath);            fisChannel = fis.getChannel();            fosChannel = fos.getChannel();            long len = fisChannel.transferTo(0, fisChannel.size(), fosChannel);        } catch (FileNotFoundException e) {            e.printStackTrace();        } catch (IOException e) {            e.printStackTrace();        } finally {            if (fisChannel != null) {                try {                    fisChannel.close();                } catch (IOException e) {                    e.printStackTrace();                }            }            if (fis != null) {                try {                    fis.close();                } catch (IOException e) {                    e.printStackTrace();                }            }            if (fosChannel != null) {                try {                    fosChannel.close();                } catch (IOException e) {                    e.printStackTrace();                }            }            if (fos != null) {                try {                    fos.close();                } catch (IOException e) {                    e.printStackTrace();                }            }        }    }

第4种,通过Files copy方式实现

private static void copyByFiles(String srcPath, String dstPath) {        Path path = Paths.get(srcPath);        FileOutputStream fos = null;        try {            fos = new FileOutputStream(dstPath);            /*             * private static final int BUFFER_SIZE = 8192;             * private static long copy(InputStream source, OutputStream sink) throws IOException             * {             * long nread = 0L;             * byte[] buf = new byte[BUFFER_SIZE];             * int n;             * while ((n = source.read(buf)) > 0) {             * sink.write(buf, 0, n);             * nread += n;             * }             * return nread;             * }             */            long len = Files.copy(path, fos);        } catch (FileNotFoundException e) {            e.printStackTrace();        } catch (IOException e) {            e.printStackTrace();        } finally {            if (fos != null) {                try {                    fos.close();                } catch (IOException e) {                    e.printStackTrace();                }            }        }    }
通过单个方法运行得出结果,IO最慢,Files.copy的速度接近transferto,但是查看源码疑惑出现了,下面列出源码Files.copy
createFile		000102400	cost	04342033631uscopyByIO		000102400	cost	254391101034132uscopyByNIO		000102400	cost	00241791703uscopyByNIOTransfer	000102400	cost	00178104807uscopyByFiles		000102400	cost	00202207341us

这是在另一台机械硬盘上的表现,第一台测试机带SSD,担心有影响,机械硬盘的结果比较明显了

5. FILE_SIZE = 102400 KBcreateFile		000102400	cost	12997817696uscopyByIO		000102400	cost	00395415564uscopyByNIO		000102400	cost	00348269525uscopyByNIOTransfer	000102400	cost	00127312664uscopyByFiles		000102400	cost	00407214806us

 

 

这么看来感觉跟IO的实现一致啊,为什么速度差别那么大呢,继续跟代码,问题在图片1中newInputStream(source)方法中

这里提供的InputSteam实际上是sun.nio.ch.ChannelInputStream,而我们测试的IO中的InputSteam是用的FileInputSteam,而ch 实际是SeekableByteChannel ,由FileSystemProvider的实现类创建,而不同操作系统会提供不同的Prodvider,比如windows的差别就在这里了。

public SeekableByteChannel newByteChannel(Path obj,                                              Set
options, FileAttribute
... attrs) throws IOException { WindowsPath file = WindowsPath.toWindowsPath(obj); WindowsSecurityDescriptor sd = WindowsSecurityDescriptor.fromAttribute(attrs); try { return WindowsChannelFactory .newFileChannel(file.getPathForWin32Calls(), file.getPathForPermissionCheck(), options, sd.address()); } catch (WindowsException x) { x.rethrowAsIOException(file); return null; // keep compiler happy } finally { sd.release(); } }

实际上Files.copy还有两种方式,一:

public static long copy(InputStream in, Path target, CopyOption... options)

关键在于

ostream = newOutputStream(target, StandardOpenOption.CREATE_NEW,                                              StandardOpenOption.WRITE);

从这里可用看出跟上面的方式基本一致,只不过一个是InputSteam,一个是OutStream。还有另外一种,二:方法如下

public static Path copy(Path source, Path target, CopyOption... options)

直接利用的是windows的复制功能

@Override    public void copy(Path source, Path target, CopyOption... options)        throws IOException    {        WindowsFileCopy.copy(WindowsPath.toWindowsPath(source),                             WindowsPath.toWindowsPath(target),                             options);    }

另外有一种方式与第二种方式类似,使用MappedByteBuffer来进行文件读写,底层是利用的mmap的机制

 

DMA(Direct Memory Access,直接内存存取) 是所有现代电脑的重要特色,它允许不同速度的硬件装置来沟通,而不需要依赖于 CPU 的大量中断负载。否则,CPU 需要从来源把每一片段的资料复制到,然后把它们再次写回到新的地方。在这个时间中,CPU 对于其他的工作来说就无法使用。

DMA 传输将数据从一个地址空间复制到另外一个地址空间。当CPU 初始化这个传输动作,传输动作本身是由 DMA 控制器来实行和完成。典型的例子就是移动一个外部内存的区块到芯片内部更快的内存区。像是这样的操作并没有让工作拖延,反而可以被重新排程去处理其他的工作。DMA 传输对于高效能 嵌入式系统算法和网络是很重要的。

DMA

在实现DMA传输时,是由DMA控制器直接掌管总线,因此,存在着一个总线控制权转移问题。即DMA传输前,CPU要把总线控制权交给DMA控制器,而在结束DMA传输后,DMA控制器应立即把总线控制权再交回给CPU。一个完整的DMA传输过程必须经过DMA请求、DMA响应、DMA传输、DMA结束4个步骤。

DMA控制器与CPU怎样分时使用内存呢?通常采用以下三种方法:

(1)停止CPU访内存;

(2)周期挪用;

(3)DMA与CPU交替访问内存。

Linux高速缓冲区

高速缓冲区在整个物理内存中的位置处于内核区和主内存区之间,不在硬盘上,之前的理解有误跟swap区搞混了。

关于零拷贝的资料

 中文版

https://www.linuxjournal.com/article/6345?page=0,2  e文版

关于mmap的资料

https://blog.csdn.net/zqixiao_09/article/details/51088478

VM_IO将VMA设置成一个内存映射IO区域。

shm与mmap的区别联系

https://blog.csdn.net/bluenet13/article/details/40039497

Linux虚拟缓存

https://www.oschina.net/translate/understanding-virtual-memory

Linux IPC,FIFO和shm

https://blog.csdn.net/pouloghost/article/details/19997961

Linux IPC

https://blog.csdn.net/a987073381/article/details/52006729

 

https://www.cnblogs.com/wang_yb/p/3351599.html

http://blog.jqian.net/post/linux-shm.html

Linux文件Cache

https://www.ibm.com/developerworks/cn/linux/l-cache/

Linux文件系统预读

https://blog.csdn.net/AXW2013/article/details/55188316

Java中的IOStatus

static final int EOF = -1;              // End of filestatic final int UNAVAILABLE = -2;      // Nothing available (non-blocking)static final int INTERRUPTED = -3;      // System call interruptedstatic final int UNSUPPORTED = -4;      // Operation not supportedstatic final int THROWN = -5;           // Exception thrown in JNI codestatic final int UNSUPPORTED_CASE = -6; // This case not supported

 

Java io的读写情况,以

磁盘—>内核缓存—>native堆——>jvm heap——>native堆——>socket缓冲——>网卡

java多了native堆和jvm heap之间拷贝就是为了防止gc发生时jvm heap内部数据地址改变,导致读取错误数据。

directbuffer和c语言中的io速度一样的,是heapbuffer拖慢了性能,不是directbuffer提高了性能。

 

你可能感兴趣的文章
Java基础篇(一)
查看>>
数据库
查看>>
mysql update与group by
查看>>
nginx反代 499 502 bad gateway 和timeout
查看>>
linux虚拟机安装tar.gz版jdk步骤详解
查看>>
python猜拳游戏
查看>>
python实现100以内自然数之和,偶数之和
查看>>
python数字逆序输出及多个print输出在同一行
查看>>
ESP8266 WIFI数传 Pixhaw折腾笔记
查看>>
苏宁产品经理面经
查看>>
百度产品经理群面
查看>>
去哪儿一面+平安科技二面+hr面+贝贝一面+二面产品面经
查看>>
element ui 弹窗在IE11中关闭时闪现问题修复
查看>>
vue 遍历对象并动态绑定在下拉列表中
查看>>
Vue动态生成el-checkbox点击无法选中的解决方法
查看>>
python __future__
查看>>
MySQL Tricks1
查看>>
python 变量作用域问题(经典坑)
查看>>
pytorch
查看>>
pytorch(二)
查看>>