传统IO与NIO(channel-to-channel)文件拷贝的探索与性能比对
Channel-to-channel传输是可以极其快速的,特别是在底层操作系统提供本地支持的时候。某些操作系统可以不必通过用户空间传递数据而进行直接的数据传输。对于大量的数据传输,这会是一个巨大的帮助。——摘抄至《Java NIO》
今天学习了NIO的channel-to-channel的数据传输方式,为了对其有一个较清晰的认识,特意进行测试,并与java-io的传统传输方式进行比较。以下将通过对我测试实例的分析来阐述其用法,并挖掘其隐藏问题点。请往下看。
一、先上代码
package nio; import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.channels.FileChannel;
import java.nio.file.Files;
import java.util.Date; /**
* 通道数据迁移
*
* @author wh-simm
*
*/
public class ChannelTransfer { public static void main(String[] args) throws Exception {
String srcFile = "E:\\videos\\奇门盾甲.2017.TC720P.国语中字.mp4";// "E:\\spring-mvc.xml";
// catFiles (Channels.newChannel (System.out), srcFile);
nioTest(srcFile, "E:\\videos\\奇门盾甲.2017.TC720P.国语中字-copy-nio.mp4");
nio2Test(srcFile, "E:\\videos\\奇门盾甲.2017.TC720P.国语中字-copy-nio2.mp4");
ioTest(srcFile, "E:\\videos\\奇门盾甲.2017.TC720P.国语中字-copy-io.mp4");
} public static void ioTest(String src, String dest) {
long time = new Date().getTime();
// 传统io处理方式
try {
Files.copy(new File(src).toPath(), new FileOutputStream(new File(dest)));
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
long run = new Date().getTime() - time;
System.out.println("io运行时长:" + run);
} public static void nioTest(String src, String dest) {
long time = new Date().getTime();
try {
catFiles(src, dest);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
long run = new Date().getTime() - time;
System.out.println("nio运行时长:" + run);
} public static void nio2Test(String src, String dest) {
long time = new Date().getTime();
try {
catFiles2(src, dest);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
long run = new Date().getTime() - time;
System.out.println("nio分段传输 运行时长:" + run);
} /**
* nio channel-channel
*
* @param target
* @param src
* @throws Exception
*/
private static void catFiles(String src, String dest) throws Exception {
FileInputStream fis = new FileInputStream(src);
FileChannel channel = fis.getChannel();
FileOutputStream fos = new FileOutputStream(dest);
// channel-to-channel 快速传递数据 超过2G的传递会被截断 int.MaxValue
channel.transferTo(0, channel.size(), fos.getChannel());
channel.close();
fos.close();
fis.close();
}
/**
* 定义2G的buffer长度
*/
private static final long bufLength = 1024 * 1024 * 1024 * 2L;// 每次拷贝2GB的数据(上限)
/**
* nio channel-channel
*
* @param target
* @param src
* @throws Exception
*/
private static void catFiles2(String src, String dest) throws Exception {
FileInputStream fis = new FileInputStream(src);
FileChannel input = fis.getChannel();
// channel-to-channel 快速传递数据 超过2G的传递会被截断 int.MaxValue
FileOutputStream fos = new FileOutputStream(dest);
FileChannel output = fos.getChannel();
long size = input.size();
while (input.position() < size) {
long count = input.transferTo(input.position(), bufLength, output);
input.position(input.position() + count);
}
input.close();
output.close();
fos.close();
fis.close();
}
}
二、测试结果展示


简要分析:从迁移日志看, 使用nio 不分段迁移方式耗时101095ms,但是有个问题 物理文件只迁移成功了2GB的数据。nio分段迁移,耗时 170428毫秒,物理文件完整迁移。传统io迁移方式耗时206286ms,物理文件完整迁移。针对迁移结果,我们首先要接收到一个信息,channel-to-channel传输时,一次只能最多只能发送2GB的数据。再者就是,nio的channel-to-channel传输大文件的速度确实比传统io方式要快。当然,这个时间偏差,性能比只能反应出在我电脑上的水准,实际上迁移过程中我的磁盘io开销已经爆满了。如果机器性能够优越,这个性能差可能会体现的更明显。
三、源码解析
3.1、ioTest方法
传统io,使用stream的一边读一边写的方式来做。这里我调用的是java.nio.file.Files.copy(Path source, OutputStream out)方法,该方法对InputStream往OutputStream中的数据写入过程进行了封装,其定义的缓存尺寸buffer-size,也是比较艺术的,8KB,即系统内存页最小分配单位。假如你还在自己写这个过程,可以考虑直接使用该方法。下面是方法内部源码,仅做参考。
public static long copy(Path source, OutputStream out) throws IOException {
// ensure not null before opening file
Objects.requireNonNull(out);
try (InputStream in = newInputStream(source)) {
return copy(in, out);
}
}
// buffer size used for reading and writing
private static final int BUFFER_SIZE = 8192;
/**
* Reads all bytes from an input stream and writes them to an output stream.
*/
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;
}
3.2、catFiles方法,不分段直接调用transferTo方法
transferTo( )和transferFrom( )方法允许将一个通道交叉连接到另一个通道,而不需要通过一个中间缓冲区来传递数据。只有FileChannel类有这两个方法,因此channel-to-channel传输中通道之一必须是FileChannel。您不能在socket通道之间直接传输数据,不过socket通道实现WritableByteChannel和ReadableByteChannel接口,因此文件的内容可以用transferTo( )方法传输给一个socket通道,或者也可以用transferFrom( )方法将数据从一个socket通道直接读取到一个文件中。
直接的通道传输不会更新与某个FileChannel关联的position值。请求的数据传输将从position参数指定的位置开始,传输的字节数不超过count参数的值。实际传输的字节数会由方法返回,可能少于您请求的字节数。
对于传输数据来源是一个文件的transferTo( )方法,如果position + count的值大于文件的size值,传输会在文件尾的位置终止。假如传输的目的地是一个非阻塞模式的socket通道,那么当发送队列(send queue)满了之后传输就可能终止,并且如果输出队列(output queue)已满的话可能不会发送任何数据。类似地,对于transferFrom( )方法:如果来源src是另外一个FileChannel并且已经到达文件尾,那么传输将提早终止;如果来源src是一个非阻塞socket通道,只有当前处于队列中的数据才会被传输(可能没有数据)。由于网络数据传输的非确定性,阻塞模式的socket也可能会执行部分传输,这取决于操作系统。许多通道实现都是提供它们当前队列中已有的数据而不是等待您请求的全部数据都准备好。
上述内容摘抄自《Java NIO》,其中提到了输出队列已满的话可能不会发送任何数据。但是没有提及输出队列的上限到底是多少。根据我的测试结果来看,这个上限是2GB,因此超过2GB的文件 不分段是无法完整传输的。这个点很重要,需要记下。
3.3、catFiles2方法,分段调用transferTo方法
这个方法是对catFiles的优化,为了方便对比,写成两个方法。主要就是定义了一个2GB的分段长度,每传输一段数据后,就标记输入通道的position,直到position到达通道的末尾。核心代码:input.position(input.position() + count)
至此,本次测试分析结束。如果再遇到大文件的拷贝等处理,你可以试试channel-to-channel方式。
传统IO与NIO(channel-to-channel)文件拷贝的探索与性能比对的更多相关文章
- 传统IO与NIO的比较
本文并非Java.io或Java.nio的使用手册,也不是如何使用Java.io与Java.nio的技术文档.这里只是尝试比较这两个包,用最简单的方式突出它们的区别和各自的特性.Java.nio提出了 ...
- 传统IO与NIO区别二
nio是new io的简称,从jdk1.4就被引入了.现在的jdk已经到了1.6了,可以说不是什么新东西了.但其中的一些思想值得我来研究.这两天,我研究了下其中的套接字部分,有一些心得,在此分享. ...
- Java IO和Java NIO 和通道 在文件拷贝上的性能差异分析
1. 在JAVA传统的IO系统中,读取磁盘文件数据的过程如下: 以FileInputStream类为例,该类有一个read(byte b[])方法,byte b[]是我们要存储读取到用户空间的缓冲区 ...
- BIO与NIO的方式实现文件拷贝
面试题 - 编程实现文件拷贝.(这个题目在笔试的时候经常出现,下面的代码给出了两种实现方案) import java.io.FileInputStream; import java.io.FileOu ...
- C# IO操作(四)大文件拷贝(文件流的使用)、文件编码
大文件拷贝(文件流的使用).文件编码 首先说一下大文件拷贝和文件流,因为计算机的内存资源是有限的,面对几个G甚至更大的文件,需要通过程序来完成拷贝,就需要用到文件流(因为我们无法做到把文件一 ...
- File IO(NIO.2):文件操作
简介 Files类是java.nio.file包的另一个主要入口点.该类提供了一组丰富的静态方法,用于读取,写入和操作文件和目录.Files方法适用于Path对象的实例.在进行其余部分之前,您应该熟悉 ...
- Java经典代码片段——使用NIO进行快速的文件拷贝
public static void fileCopy(File in, File out) throws IOException { FileChannel inChannel = new File ...
- netty1---传统IO和NIO的区别
传统IO: package OIO; import java.io.IOException; import java.io.InputStream; import java.net.ServerSoc ...
- NIO之通道(Channel)的原理与获取以及数据传输与内存映射文件
通道(Channel) 由java.nio.channels包定义的,Channel表示IO源与目标打开的连接,Channel类似于传统的“流”,只不过Channel本身不能直接访问数据,Channe ...
随机推荐
- C语言知识点总结
本文采用思维导图的方式撰写,更好的表述了各知识点之间的关系,方便大家理解和记忆.这个总结尚未包含C语言数据结构与算法部分,后续会陆续更新出来,文中有漏掉的知识点,还请大家多多指正.
- 利用mk-table-checksum监测Mysql主从数据一致性操作记录
前面已经提到了mysql主从环境下数据一致性检查:mysql主从同步(3)-percona-toolkit工具(数据一致性监测.延迟监控)使用梳理今天这里再介绍另一种Mysql数据一致性自动检测工具: ...
- freemarker常用值格式化方法
freemarker常用的值格式化方法: 1.${price?string('0.00')} 对price进行格式化,小数点后不足2位用0补足. 比如:price=1 输出:1.00 2.${pric ...
- Linux常用命令(一)--系统命令
命令字 命令字 [命令选项] [命令参数] 1. 命令中所有字符区分大小写 2. 命令选项分为短格式(-)及长格式(–) 3. 必须在命令行提示符下输入命令 4. 命令中的各个部分至少需要一个空格分隔 ...
- Codeforce D. Make a Permutation!
D. Make a Permutation! time limit per test 2 seconds memory limit per test 256 megabytes input stand ...
- htmlcss渐变及兼容性
自我总结,欢饮拍砖. <!DOCTYPE HTML> <html lang="en"> <head> <meta content ...
- 3.移植驱动到3.4内核-移植DM9000C驱动
在上章-使内核支持烧写yaffs2,裁剪内核并制作补丁了 本章,便开始移植以前2.6内核的驱动到3.4新内核 1.介绍 首先内核更新,有可能会重新定义新的宏,去除以前的宏,以前更改函数名等 所以移植驱 ...
- Python CRM项目四
实现Django Admin的多对多的复选框效果 效果:左边显示的是未选中的字段,右边显示的是已选中的字段,两边点击的标签可以互相更换 首先在king_admin.py中增加filter_horizo ...
- Python学习笔记(二):字典
字典由多个键及与其对应的值构成的键值对构成,字典中键唯一,值不唯一. 1)dict 函数: >>>items=[('name','lilei'),('age',12)] >&g ...
- ssh 设置私钥实现两台linux主机无密码访问
在服务器主机上(称为A主机) 创建公钥与私钥: ssh-keygen -t rsa 一路回车,如果想设置密码短语,在提示 passphrase 的时候设置密码短语 查看生成的公钥及私钥: ls ~/. ...