Java IO和Java NIO在文件拷贝上的性能差异分析(java IO优化)_LY破晓的博客-程序员宝宝

技术标签: 工具类  java  io  优化  文件上传  

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。

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/qq_40321119/article/details/108448125

智能推荐

Android 5.0以上操作外置sdcard(一)_zwyAndroid的博客-程序员宝宝

google在android4.4封闭了操作外置sd卡的功能,业界一片吐槽,so,在android5.0提供了全新的方式去操作外置sdcard。    1.首先获得外置sdcard的路径:        extSdcardPath = System.getenv("SECONDARY_STORAGE");    2.获取uri,防止重复进入documentui界面            if (!...

Ansible自动化<二>_神慕蔡蔡的博客-程序员宝宝

官方帮助文档:https://docs.ansible.com/ansible-core/2.11/installation_guide/intro_installation.html1.Ansible配置文件More Actions配置文件或指令描述/etc/ansible/ansible.cfg主配置文件/etc/ansible/hosts主机清单/usr/bin/ansible主程序,临时命令执行工具/usr/bin/ansible主程序,临时命

NodeJS中使用Sequelize连接MySQL数据库,多对多关联中,查询出来的数据不正确的问题_小歲月丶太着急的博客-程序员宝宝

情景:在NodeJS中开发RestApi接口,用的是Sequelize连接MySQL,遇到的问题是查询的时候出来的奇葩结果如下:预期中的结果应该是如下:[ { "id": 3, "title": "标题3", "content": "内容3", "createdAt": "2021-03-30T06:39:30.000Z", "updatedAt": "2021-03-30T06:39:30.000Z", "tags": [ {

高起播低延时视频流媒体播放器互联网无插件微信H5直播方案EasyPlayer-RTSP—播放不了外网RTSP流问题解析_TSINGSEE的博客-程序员宝宝

背景分析一般对于一个播放器,应该支持如下几种显示模式: 等比例,最大化区域显示,不裁剪 等比例,最大区域显示,裁剪 拉伸显示,铺满全屏要实现这几种显示模式。其实只要对播放控件的布局进行些许调整即可。同时针对用户需求,不断提升播放性能。今天主要为大家介绍EasyPlayer-RTSP解决播放不了外网RTSP流的问题提出问题最近有客户反应EasyPlayer-RTS...

华为OJ-质数因子-C++实现/Java实现_段刘昌的博客-程序员宝宝

质数因子题目描述功能:输入一个正整数,按照从小到大的顺序输出它的所有质数的因子(如180的质数因子为2 2 3 3 5 )最后一个数后面也要有空格详细描述:函数接口说明:public String getResult(long ulDataInput)输入参数:long ulDataInput:输入的正整数返回值:String输入描述:输入一个long型整数输出描述:按照从小到大的顺序输出它的所有...

SSL和TLS-SSL Protocol介绍_此心光明-超然的博客-程序员宝宝_ssl_protocols

SSL和TLS-SSL Protocol介绍SSL协议是客户端/服务器协议,向双方提供基本的安全服务:认证(端和数据来源认证)连接机密性服务连接完整性服务(不能恢复)尽管SSL协议使用公钥(public key)加密,它不提供源(origin)和完整性的不可否认(nonrepudiation)服务。SSL协议是sockets-oriented,意味着通过网络socket发送或者接收...

随便推点

python 判断dict中是否含有某个key_Tangzongyu123的博客-程序员宝宝_python 判断dict是否包含某个key

方法一 dict.has_key(key_name) 返回值:True 或者 False方法二if key in dict.keys(): print "key in dict"

学习了解ThreadLocal_机智皮皮涛的博客-程序员宝宝

ThreadLocalThrteadLocal来提供线程内局部变量,这种变量在多线程访问时,可以保证每个线程变量互相独立.​ 这种变量在线程生命周期内起作用,可以在不同组件中传递,类似于Javaweb 的Context 域.常用方法:方法说明ThreadLocal<?>()创建对象set(T value)设置当前线程绑定的局部变量T get()获取变量remove()移除与当前线程绑定的变量Thread 与Synchornized的区别

UML类图(Class Diagram)中类与类之间的关系及表示方式_eknown的博客-程序员宝宝

类之间大体分为5种关系: 1,依赖关系(Dependency)单向,表示一个类依赖于另一个类的定义,其中一个类的变化将影响另外一个类,是一种“use a”关系如果A依赖于B,则B表现为A的局部变量,方法参数,静态方法调用等[java] view plain copypublic class Person {      public void doSomething(){          Card...

POJ 1733 Parity game (HASH+并查集)_weixin_33854644的博客-程序员宝宝

Parity gameTime Limit: 1000MS Memory Limit: 65536KTotal Submissions: 5168 Accepted: 2028DescriptionNow and then you play the following game with your friend. Your f...

【HDU】1159 Common Subsequence(DP、最长公共子序列)_xuziling_的博客-程序员宝宝

【HDU】1159 Common Subsequence (DP、最长公共子序列)DP,dp[i][j]表示a[0]-a[i]和b[0]-b[j]间的最长公共子序列。打表可知:当a[i]==b[j]时,dp[i][j]=dp[i-1][j-1]+1; 当a[i]!=b[j]时,dp[i][j]=max(dp[i-1][j],dp[i][j-1]).

Atitit 实现java的linq 以及与stream api的比较_attilax的博客-程序员宝宝

Atitit实现java的linq以及与stream api的比较 1.1. Linq 和stream api的关系,以及主要优缺点 11.2. Linq 与stream api的适用场景 11.3. Java中的linq 1  Linq来源于sql,而我们知道在数据查询领域,sql是标准化的一个dsl。。1.1. Linq 和stream api的关系