【Java基础知识 18】通过FileUtils.copyFile探索IO原理

article/2025/9/19 10:04:31

目录

    • 一、FileUtils.copyFile
      • 1、从实例出发
      • 2、还是蛮快的,探索源码一番...
    • 二、FileChannel
      • 1、读操作
      • 2、写操作
      • 3、代码实例
      • 4、控制台输出
    • 三、如何减少copy和上下文切换的次数?
      • 1、为什么不能舍弃内核空间这一步,直接读取到用户空间呢?
      • 2、如何减少copy和上下文切换的次数?
      • 3、代码实例
      • 4、控制台输出
      • 哪吒精品系列文章

一、FileUtils.copyFile

1、从实例出发

一般开发的时候,都是通过文件工具类进行文件的copy,那么它的性能怎么样呢?它是怎么实现的呢?今天就来分析以下FileUtils.copyFile。

private static void copyFileByUtils() {String srcFilePath = "H:\\CSDN\\JWFS.rar";// 文件大小 68.8 MBString destFilePath = "H:\\CSDN\\netty\\nio\\JWFS.rar";long start = System.currentTimeMillis();try {FileUtils.copyFile(new File(srcFilePath),new File(destFilePath));} catch (IOException e) {e.printStackTrace();}long end = System.currentTimeMillis();System.out.println("copyFileByUtils 消耗:"+(end-start)+"毫秒");}

在这里插入图片描述

2、还是蛮快的,探索源码一番…

private static void doCopyFile(File srcFile, File destFile, boolean preserveFileDate) throws IOException {if (destFile.exists() && destFile.isDirectory()) {throw new IOException("Destination '" + destFile + "' exists but is a directory");} else {FileInputStream fis = null;FileOutputStream fos = null;FileChannel input = null;FileChannel output = null;try {fis = new FileInputStream(srcFile);fos = new FileOutputStream(destFile);input = fis.getChannel();output = fos.getChannel();long size = input.size();long pos = 0L;for(long count = 0L; pos < size; pos += output.transferFrom(input, pos, count)) {count = size - pos > 31457280L ? 31457280L : size - pos;}} finally {IOUtils.closeQuietly(output);IOUtils.closeQuietly(fos);IOUtils.closeQuietly(input);IOUtils.closeQuietly(fis);}if (srcFile.length() != destFile.length()) {throw new IOException("Failed to copy full contents from '" + srcFile + "' to '" + destFile + "'");} else {if (preserveFileDate) {destFile.setLastModified(srcFile.lastModified());}}}}

发现了一个生僻的词汇,FileChannel,研究一下。

二、FileChannel

在这里插入图片描述
这种方式是“在非直接缓冲区中,通过Channel实现文件的复制”

1、读操作

  1. 将磁盘文件读取到操作系统OS提供的内核地址空间的内存中,第一次复制,OS上下文切换到内核模式;
  2. 将内核地址空间内存中的文件内容复制到JVM提供的用户地址空间的内存中,第二次复制,OS上下文切换到用户模式;

2、写操作

  1. 将用户地址空间的JVM内存中的文件内容复制到OS提供的内核地址空间中的内存中,第一次复制,OS上下文切换到内核模式;
  2. 将内核地址空间中内存的文件内容写入磁盘文件,第二次复制,写入操作完毕后,OS上下文最终切换到用户模式;

JVM控制的内存称为堆内内存,一般用Java操作的内存都属于堆内内存,堆内内存由JVM统一管理,根据上面的流程图可以发现,一次文件的读写要经过4次copy和4次用户控件与内核空间的上下文切换。

3、代码实例

package com.guor.demo.io;import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;public class CopyFileTest {private static void copyFileByChannel() {FileInputStream fileInputStream = null;FileOutputStream fileOutputStream = null;FileChannel intChannel = null;FileChannel outChannel = null;String srcFilePath = "H:\\CSDN\\JWFS.rar";// 文件大小 68.8 MBString destFilePath = "H:\\CSDN\\netty\\nio\\JWFS.rar";long start = System.currentTimeMillis();try {fileInputStream = new FileInputStream(srcFilePath);fileOutputStream = new FileOutputStream(destFilePath);// 获取通道intChannel = fileInputStream.getChannel();outChannel = fileOutputStream.getChannel();// 创建非直接缓冲区ByteBuffer buffer = ByteBuffer.allocate(1024);while (intChannel.read(buffer)!=-1){buffer.flip();outChannel.write(buffer);buffer.clear();}long end = System.currentTimeMillis();System.out.println("copyFileByChannel 消耗:"+(end-start)+"毫秒");}catch (Exception e){System.out.println(e);}finally {if(outChannel!=null){try {outChannel.close();} catch (IOException e) {}}if(intChannel!=null){try {intChannel.close();} catch (IOException e) {}}if(fileOutputStream!=null){try {fileOutputStream.close();} catch (IOException e) {}}if(fileInputStream!=null){try {fileInputStream.close();} catch (IOException e) {}}}}public static void main(String[] args) {copyFileByChannel();}
}

4、控制台输出

在这里插入图片描述
感觉和FileUtils.copyFile的速度还是有差距的。

三、如何减少copy和上下文切换的次数?

1、为什么不能舍弃内核空间这一步,直接读取到用户空间呢?

因为JVM中有GC,GC会不定期的清理没用的对象,并且压缩文件区域,如果某一时刻正在JVM中复制一个文件,但由于GC的压缩操作可能会引起文件在JVM中的位置发生改变,进而导致程序出现异常。因此,为了保证文件在内存中的位置不发生改变,只能将其放入OS的内存中。

2、如何减少copy和上下文切换的次数?

使用直接缓冲区,就可以在JVM中通过一个address变量指向OS中的一块内存(称为物理映射文件),之后,就可以通过JVM直接使用OS中的内存。

下面介绍一个新的方式“在直接缓冲区中,通过Channel实现文件的复制”
在这里插入图片描述
这样,数据的赋值操作都是在内核空间里进行的,用户空间与内核空间直接的复制次数为0,也就是零拷贝。

3、代码实例

private static void copyFileByMapped() {String srcFilePath = "H:\\CSDN\\JWFS.rar";// 文件大小 68.8 MBString destFilePath = "H:\\CSDN\\netty\\nio\\JWFS.rar";long start = System.currentTimeMillis();FileChannel inChannel = null;FileChannel outChannel = null;try {// 文件的输入通道inChannel = FileChannel.open(Paths.get(srcFilePath), StandardOpenOption.READ);// 文件的输出通道outChannel = FileChannel.open(Paths.get(destFilePath), StandardOpenOption.WRITE, StandardOpenOption.READ, StandardOpenOption.CREATE);// 输入通道和输出通道之间的内存映射文件,内存映射文件处于堆外内存MappedByteBuffer inMappedBuffer = inChannel.map(FileChannel.MapMode.READ_ONLY, 0, inChannel.size());MappedByteBuffer outMappedBuffer = outChannel.map(FileChannel.MapMode.READ_WRITE, 0, inChannel.size());// 直接对内存映射文件进行读写byte[] bytes = new byte[inMappedBuffer.limit()];inMappedBuffer.get(bytes);outMappedBuffer.put(bytes);long end = System.currentTimeMillis();System.out.println("copyFileByMapped 消耗:"+(end-start)+"毫秒");}catch (Exception e){System.out.println(e);}finally {try {inChannel.close();outChannel.close();} catch (IOException e) {}}}

4、控制台输出

在这里插入图片描述
直接缓冲区拷贝文件比非直接缓冲区拷贝文件,快了整整10倍。

哪吒精品系列文章

Java学习路线总结,搬砖工逆袭Java架构师

10万字208道Java经典面试题总结(附答案)

SQL性能优化的21个小技巧

Java基础教程系列

Spring Boot 进阶实战
在这里插入图片描述


http://chatgpt.dhexx.cn/article/EAmZ5Gjb.shtml

相关文章

FileUtils工具类常用方法

文件操作工具类&#xff08;FileUtils&#xff09; 使用 apache 的commons-io包下的FileUtils&#xff0c;import org.apache.commons.io.FileUtils; 下载地址&#xff1a;http://commons.apache.org/proper/commons-io/download_io.cgi 官方API文档&#xff1a;http://com…

App分渠道打包工具

渠道包就是要在安装包中添加渠道信息&#xff0c;也就是channel&#xff0c;对应不同的渠道&#xff0c;例如&#xff1a;小米市场、360市场、应用宝市场等。 我们要在安装包中添加不同的标识&#xff0c;应用在请求网络的时候携带渠道信息&#xff0c;方便后台做运营统计&…

H5打包成app的在线工具

H5打包成APP&#xff0c;有两种方式&#xff0c;方式一是直接用网址打包&#xff0c;方式二是将H5文件打包到APP的资源文件里面。第一种方式的用户体验不是很好&#xff0c;因为这种APP在用户没有网络的情况下&#xff0c;打开APP就会变成白屏&#xff0c;因为这种远程网址调用…

网站打包成app,webapp在线打包工具推荐

最近因为需求&#xff0c;需要把移动端网页打包成APP&#xff0c;本人一直是做网站开发的&#xff0c;没想到现在的webapp打包能如此方便了&#xff0c;打包的时候只用提供网站链接就可以了&#xff08;原理应该是做一个app简单浏览器&#xff0c;然后默认打开你网站的链接&…

HTML一键打包IPA(苹果IOS应用)工具 网站打包 APP

工具简介 HTML一键打包IPA&#xff08;苹果应用&#xff09;工具可以把本地HTML项目或者网站打包为一个苹果应用IPA文件&#xff0c;无需编写任何代码&#xff0c;支持在苹果设备上安装运行。 打包工具群&#xff1a;429338543 下载地址&#xff1a; 点击进入下载页面 加群获…

HTML一键打包APK工具_安卓app封装_H5打包安卓APP

随着目前苹果Appstore审核越来越严格&#xff0c;每天平均上架1000个&#xff0c;下架3000个应用&#xff0c;想要上架苹果应用商店已经越来越困难了&#xff0c;反复修改审核上架&#xff0c;短则1-2周&#xff0c;长则几个月&#xff0c;并且游戏类应用上架目前极其困难。 因…

AndroidStudio如何打包APP

首先&#xff0c;点击AS工具栏的Build下面的“Generate Signed Build APK…” 然后在弹出的框内选择APK &#xff08;Android App Bundle&#xff1a;用于通过 Google Play 发布的应用&#xff0c;需要升级到AS 3.2 以上版本才支持App Bundle格式&#xff1b; APK&#xff1a;…

网站项目打包成app

web项目打包app 这次打包app项目&#xff0c;主要用到的软件是HBuilderX&#xff1b; HBuilderX下载网址&#xff1a;https://www.dcloud.io/hbuilderx.html HBuilderX&#xff1a;可直接将网页打包成手机端app&#xff0c;可以有安卓和苹果两种安装包&#xff0c;这次我们主…

Flutter项目打包生成APK

flutter实现安卓打包&#xff1a;&#xff08;以安卓Studio工具为例&#xff09; &#xff08;1&#xff09;创建key.jks文件 在安卓studio中调整至项目路径&#xff0c;例如&#xff1a; 我的项目所在地 E:\Flutter\fluttershuqi>然后输入命令&#xff1a; keytool -ge…

Android App打包流程

简单总结下app打包的流程&#xff1a; 第一步&#xff1a;aapt 为res目录下的资源生成R.java文件&#xff0c;同时为AndroidManinfest.xml生成Manifest.java文件 第二步&#xff1a;aidl 把项目中自定义的aidl文件生成相应的Java代码文件 第三步&#xff1a;javac 把项目中所…

iOS app打包过程

1.点击Product - Archive 2.选择Development 点击Next 3.什么都不选&#xff0c;点击下一步 4.选择第一个&#xff0c;点击next 5.选择Export 6.拿到.ipa文件&#xff0c;导出成功&#xff01; 7.接下来&#xff0c;可以将ipa文件拖到蒲公英进行发布

Flutter 打包APP (Android IOS)

打包Android apk 参考 https://flutter.dev/docs/deployment/android https://flutterchina.club/android-release/ Flutter项目打包成安卓apk详解来了&#xff08;解决安装没网络问题&#xff09; 【Flutter 专题】39 图解 iOS 打包 IPA 文件 Flutter - 打包APK、IPA 及 IOS上…

【uniapp小程序】—— APP项目云打包(安卓)

&#x1f341; 前言 之前小程序系列文章写了配置页面和封装自定义组件&#xff0c;这次写一下开发完成我们的项目后&#xff0c;如何进行打包安装。 本文主要讲述的是使用 uniapp打包安卓。 &#x1f342; 正文 第一步&#xff1a;查看自己的项目的基础配置 第二步&#xff1a…

前端打包利器,webpack工具,app打包工具

什么是 webpack&#xff1f; webpack是近期最火的一款模块加载器兼打包工具&#xff0c;它能把各种资源&#xff0c;例如JS&#xff08;含JSX&#xff09;、coffee、样式&#xff08;含less/sass&#xff09;、图片等都作为模块来使用和处理。 我们可以直接使用 require(XXX) 的…

app打包流程

在项目根目录 npm run build 会多出一个dist文件 打开hubuildX 文件–》新建 —》项目 5app—》模板默认模板–》创建 --》 项目根目录右键 --》在外部资源打开 --》找到刚才打包的dist文件里的所有文件到这个新建的项目里面 —》 全部替换 --》 把css image js 文件夹删…

干货,快速的教你如何打包app

所需工具&#xff1a;HbuilderX hbuilderX下载地址&#xff0c;下载符合自己电脑的就行。 1.新建项目 步骤&#xff1a;文件 → 新建 → 项目 2.选择5app,输入项目名称&#xff0c;选择项目存放目录 3.和我们正常的项目一样&#xff0c;只不过多了 unpackage 和 manifest.j…

HTML一键打包APK工具(安卓应用APP)

工具简介 “HMTL一键打包APK工具”可以把本地HTML项目或者网站打包为一个安卓应用APK文件&#xff0c;无需编写任何代码&#xff0c;支持在安卓设备上安装运行。 打包工具群&#xff1a;429338543 下载地址&#xff1a; 点击进入下载页面 加群获取最新软件 软件交流群&#…

【 uniapp 】打包Android的apk(原生APP-云打包),及发布测试

前言&#xff1a; 跨端(小程序、Android、IOS)项目开发好了&#xff0c;我们如何去利用 uniapp 的云打包去打包 apk 文件&#xff0c;然后上传测试呢&#xff1f;今天我们一起来学习一下&#xff0c;一步一步如何实现&#xff01; 目录 一、 打包 Android &#xff0c;生成apk…

一款好用的应用程序打包工具

工具简介 Inno Setup用Delphi写成&#xff0c;其官方网站同时也提供源程序免费下载。Inno Setup是一个免费的安装制作软件&#xff0c;小巧、简便、精美是其最大特点&#xff0c;支持pascal脚本&#xff0c;能快速制作出标准Windows2000风格的安装界面&#xff0c;足以完成一般…

快速打包、发布和管理应用——AppUploader工具介绍

AppUploader的主要功能介绍 购买激活码 在AppUploader官网上&#xff0c;可以购买激活码激活账号&#xff0c;根据需求购买&#xff0c;单次购买多个比单次购买单个更划算。 激活激活码 购买激活码后&#xff0c;可以在激活页面输入订单进行查询激活码&#xff0c;从而激活账…