Java IO和Java NIO 和通道 在文件拷贝上的性能差异分析
1. 在JAVA传统的IO系统中,读取磁盘文件数据的过程如下:
以FileInputStream类为例,该类有一个read(byte b[])方法,byte b[]是我们要存储读取到用户空间的缓冲区。参看read(byte b[])方法的源码,可知,它会在内部再调用readBytes(b, 0, b.length)方法,而且readBytes(b, 0, b.length)方法是一个native方法(即本地方法),最终通过这个本地方法来发起一次系统调用,即调用系统内核的read()方法,内核从磁盘读取数据到内核缓冲区,这个过程由磁盘控制器通过DMA操作将数据从磁盘读取取内核缓冲区,此过程不依赖于CPU。然后用户进程再将数据从内核缓冲区拷贝到用户空间缓冲区。用户进程再从用户空间缓冲区中读取数据。因为用户进程是不可以直接访问硬件的。所以需要通过内核来充当中间人的作用来实现文件的读取。整个过程如下图所示:

2. 自从JAVA 1.4以后,JAVA在NIO在引入了文件通道的概念,在API中有提供了一个FileChannel类。该类与传统的IO流进行关联。可以由FileInputStream或FileOutputStream获取该文件通道,我们可以通过通道对文件进行读写操作。
3.JAVA NIO中还引入了文件内存映射的概念:现代操作系统大都支持虚拟内存映射,这样,我们可以把内核空间地址与用户空间的虚拟地址映射到同一个物理地址,这样,DMA 硬件(只能访问物理内存地址)就可以填充对内核与用户空间进程同时可见的缓冲区了。如下图所示:

这样做的好处是,我们在读取磁盘文件时,再也不用通过内核缓冲区到用户进程缓冲区的来回拷贝操作了。操作系统会通过一些页面调度算法来将磁盘文件载入对分页区进行高速缓存的物理内存。我们就可以通过映射后物理内存来读取磁盘文件了。
4. 下面我们通过三种不同方式文件拷贝的案例来验证文件通道及文件内存映射在IO系统中的作用。测试环境为windows 32位系统和JDK1.6。代码中使用的测试文件movie.avi为一个123MB的视频文件。代码如下:
package cn.com.hbust.nio.file;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
public class FileCopyTest {
public static void main(String[] args) throws Exception {
String sourcePath = "F:\\mywork\\javademo\\dir1\\movie.avi";
String destPath1 = "F:\\mywork\\javademo\\dir2\\movie1.avi";
String destPath2 = "F:\\mywork\\javademo\\dir2\\movie2.avi";
String destPath3 = "F:\\mywork\\javademo\\dir2\\movie3.avi";
long t1 = System.currentTimeMillis();
traditionalCopy(sourcePath,destPath1);
long t2 = System.currentTimeMillis();
System.out.println("传统IO方法实现文件拷贝耗时:" + (t2-t1) + "ms");
nioCopy(sourcePath,destPath2);
long t3 = System.currentTimeMillis();
System.out.println("利用NIO文件通道方法实现文件拷贝耗时:" + (t3-t2) + "ms");
nioCopy2(sourcePath,destPath3);
long t4 = System.currentTimeMillis();
System.out.println("利用NIO文件内存映射及文件通道实现文件拷贝耗时:" + (t4-t3) + "ms");
}
private static void nioCopy2(String sourcePath, String destPath) throws Exception {
File source = new File(sourcePath);
File dest = new File(destPath);
if(!dest.exists()) {
dest.createNewFile();
}
FileInputStream fis = new FileInputStream(source);
FileOutputStream fos = new FileOutputStream(dest);
FileChannel sourceCh = fis.getChannel();
FileChannel destCh = fos.getChannel();
MappedByteBuffer mbb = sourceCh.map(FileChannel.MapMode.READ_ONLY, 0, sourceCh.size());
destCh.write(mbb);
sourceCh.close();
destCh.close();
}
private static void traditionalCopy(String sourcePath, String destPath) throws Exception{
File source = new File(sourcePath);
File dest = new File(destPath);
if(!dest.exists()) {
dest.createNewFile();
}
FileInputStream fis = new FileInputStream(source);
FileOutputStream fos = new FileOutputStream(dest);
byte [] buf = newbyte [512];
int len = 0;
while((len = fis.read(buf)) != -1) {
fos.write(buf, 0, len);
}
fis.close();
fos.close();
}
private static void nioCopy(String sourcePath, String destPath) throws Exception{
File source = new File(sourcePath);
File dest = new File(destPath);
if(!dest.exists()) {
dest.createNewFile();
}
FileInputStream fis = new FileInputStream(source);
FileOutputStream fos = new FileOutputStream(dest);
FileChannel sourceCh = fis.getChannel();
FileChannel destCh = fos.getChannel();
destCh.transferFrom(sourceCh, 0, sourceCh.size());
sourceCh.close();
destCh.close();
}
}
每执行完一次拷贝之后,将F:\mywork\javademo\dir2\目录中的内容删除掉,重复执行8次。观察测试结果如下:时间单位为ms(毫秒)

由上表可知,传统IO方式平均拷贝完成时间约为1968ms,NIO文件通道方式平均拷贝完成时间约为1672ms,文件内存映射及文件通道方式平均拷贝完成时间约为1418ms。
转载自http://www.open-open.com/lib/view/open1413518521372.html
Java IO和Java NIO 和通道 在文件拷贝上的性能差异分析的更多相关文章
- Java IO和Java NIO在文件拷贝上的性能差异分析
1. 在JAVA传统的IO系统中,读取磁盘文件数据的过程如下: 以FileInputStream类为例,该类有一个read(byte b[])方法,byte b[]是我们要存储读取到用户空间的缓冲区 ...
- hive运行query语句时提示错误:org.apache.hadoop.ipc.RemoteException: java.io.IOException: java.io.IOException:
hive> select product_id, track_time from trackinfo limit 5; Total MapReduce jobs = 1 Launching Jo ...
- java.io.OutputStream & java.io.FileOutputStream
java.io.OutputStream & java.io.FileOutputStream 1.Java.io.OutputStream(字节输出流) 字节输出流,这是一个抽象类,是表示输 ...
- hadoop报错:java.io.IOException(java.net.ConnectException: Call From xxx/xxx to xxx:10020 failed on connection exception: java.net.ConnectException: 拒绝连接
任务一直报错 现象比较奇怪,部分任务可以正常跑,部分问题报错 报错信息如下: Ended Job = job_1527476268558_132947 with exception 'java.io. ...
- java.io.IOException: java.sql.SQLException: ORA-01502: index 'BTO.PK_xxxxx' or partition of such index is in unusable state
最近由于数据库的全备出问题了,所以一直在观察. 刚好发现很多不需要的数据,就删了几百个G的数据吧. 今天突然就报这个问题. java.io.IOException: java.sql.SQLExcep ...
- ERROR tool.ImportTool: Import failed: java.io.IOException: java.lang.ClassNotFoundException: org.apache.hadoop.hive.conf.HiveConf
sqoop从mysql导入到hive报错: 18/08/22 13:30:53 ERROR tool.ImportTool: Import failed: java.io.IOException: j ...
- sqoop mysql--->hive 报错 (ERROR tool.ImportTool: Import failed: java.io.IOException: java.lang.ClassNotFoundException: org.apache.hadoop.hive.conf.HiveConf)
ERROR tool.ImportTool: Import failed: java.io.IOException: java.lang.ClassNotFoundException: org.apa ...
- Hive报错 Failed with exception java.io.IOException:java.lang.IllegalArgumentException: java.net.URISyntaxException: Relative path in absolute URI: ${system:user.name%7D
报错信息如下 Failed with exception java.io.IOException:java.lang.IllegalArgumentException: java.net.URISyn ...
- Intellij IDEA报错:Could not save application settings: java.io.IOException: java.lang.AssertionError: Unexpected content storage modificat
Question: i have a message saying "Could not save application settings: java.io.IOException: ja ...
随机推荐
- 数据挖掘算法——Apriori算法
Apriori算法 首先,Apriori算法是关联规则挖掘中很基础也很经典的一个算法. 转载来自:链接:https://www.jianshu.com/p/26d61b83492e 所以做如下补充: ...
- 开源搜索引擎solr elasticsearch学习计划
其实不单单是研究solr elasticsearch把,进行调研性技术学习时,应该制定一些目标以及里程碑.新的技术调研 学习是一件很爽的事,能学到新技术新东西.但是在学习新技术同时,有几个问题是需要我 ...
- [jzoj]2505.【NOIP2011模拟7.29】藤原妹红
Link https://jzoj.net/senior/#main/show/2505 Description 在幻想乡,藤原妹红是拥有不老不死能力的人类.虽然不喜欢与人们交流,妹红仍然保护着误入迷 ...
- bzoj2982: combination(lucas定理板子)
2982: combination Time Limit: 1 Sec Memory Limit: 128 MBSubmit: 664 Solved: 397[Submit][Status][Di ...
- helm-chart5,模板和访问文件
提供的一些声明和使用命名模板段的操作: define在模板中声明一个新的命名模板 template导入一个命名模板 block 声明了一种特殊的可填写模板区域 首先,模板名称是全局的.如果声明两个具有 ...
- buffer格式的转换
---恢复内容开始--- 定义好一个buffer 例如: var buf = new Buffer(''nihaoya); 通过str转成base64的字符 var str =buf.toString ...
- Linux内核笔记:epoll实现原理(一)
一.说明 针对的内核版本为4.4.10. 本文只是我自己看源码的简单笔记,如果想了解epoll的实现,强烈推荐下面的文章: The Implementation of epoll(1) The Imp ...
- __x__(13)0906第三天__xhtml 的语法规范
html中不区分大小写,但是最好用小写. 注释不能嵌套. html标签必须结束,即 <></> 或者 < /> html标签不能交叉嵌套. html的标签,有 ...
- Python练手例子(4)
16.一个数如果恰好等于它的因子之和,这个数就称为"完数".例如6=1+2+3.编程找出1000以内的所有完数. 程序分析:请参照程序Python 100例中的第14个例子 #py ...
- js中函数创建的三种方式
1.函数声明 function sum1(n1,n2){ return n1+n2; }; 2.函数表达式,又叫函数字面量 var sum2=function(n1,n2){ re ...