第一种,传统的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 04342033631us
copyByIO 000102400 cost 254391101034132us
copyByNIO 000102400 cost 00241791703us
copyByNIOTransfer 000102400 cost 00178104807us
copyByFiles 000102400 cost 00202207341us
这是在另一台机械硬盘上的表现,第一台测试机带SSD,担心有影响,机械硬盘的结果比较明显了
5. FILE_SIZE = 102400 KB
createFile 000102400 cost 12997817696us
copyByIO 000102400 cost 00395415564us
copyByNIO 000102400 cost 00348269525us
copyByNIOTransfer 000102400 cost 00127312664us
copyByFiles 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<? extends OpenOption> 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传输前,CPU要把总线控制权交给DMA控制器,而在结束DMA传输后,DMA控制器应立即把总线控制权再交回给CPU。一个完整的DMA传输过程必须经过DMA请求、DMA响应、DMA传输、DMA结束4个步骤。
DMA控制器与CPU怎样分时使用内存呢?通常采用以下三种方法:
(1)停止CPU访内存;
(2)周期挪用;
(3)DMA与CPU交替访问内存。
Linux高速缓冲区
高速缓冲区在整个物理内存中的位置处于内核区和主内存区之间,不在硬盘上,之前的理解有误跟swap区搞混了。
https://www.cnblogs.com/alantu2018/p/8447411.html
关于零拷贝的资料
https://www.jianshu.com/p/fad3339e3448 中文版
https://blog.csdn.net/hzrandd/article/details/51025341
https://blog.csdn.net/linsongbin1/article/details/77650105
https://blog.csdn.net/a417930422/article/details/52585862
https://www.linuxjournal.com/article/6345?page=0,2 e文版
https://www.ibm.com/developerworks/cn/java/j-zerocopy/#fig1
关于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 file
static final int UNAVAILABLE = -2; // Nothing available (non-blocking)
static final int INTERRUPTED = -3; // System call interrupted
static final int UNSUPPORTED = -4; // Operation not supported
static final int THROWN = -5; // Exception thrown in JNI code
static 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提高了性能。