本文共 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提高了性能。