java拷贝文件


title : 每日深耕,勤练不缀之java拷贝文件(copy)及buffer的基础使用

拷贝实现方式:
1.java.io类库,为源文件构建一个FileputStream读取,为目标文件构建FileOutputStream,完成写入工作

import java.io.*;

public class copyFileByStream {
    public static void copyFileByStream(File source,File dest) throws IOException{
        try(InputStream is  = new FileInputStream(source);
            OutputStream os = new  FileOutputStream(dest);){
            byte[] buffer =new byte[1024];
            int length;
            while((length =is.read(buffer))>0){
                os.write(buffer,0,length);
            }
        }

    }
    /**
     * 利用NIO实现
     */
}

2.java.nio类库提供的transferTo或者transferFrom方法实现

/**
     * 利用NIO实现
     */
    public static void copyFileByChannel(File source,File dest) throws IOException{
        try(FileChannel sourceChannel=new FileInputStream(source).getChannel();
            FileChannel targetChannel =new FileOutputStream(dest).getChannel();
        ){
            for(long count =sourceChannel.size();count>0;){
                long transferred =sourceChannel.transferTo(sourceChannel.position(),count,targetChannel);
                sourceChannel.position(sourceChannel.position()+transferred);
                count-=transferred;
            }
        }

主要还是为了让你加深对java类库设计和实现的了解
零copy
值得注意的点:
1.不同的copy方式,底层机制有什么区别?
2.为什么零拷贝(zero-copy)可能有性能优势?
3.Buffer分类和使用
4.Direct Buffer对垃圾收集等方面的影响与实践选择

拷贝实现机制
当我们使用输入输出流的时候,实际上进行了多次上下文切换。
比如应用读取数据时,先在内核态将数据从磁盘读取到内核缓存,再切换到用户态将数据从内核缓存读取到用户缓存。
写入数据时步骤相反

在这里插入图片描述

transfer To的传输过程
在这里插入图片描述

基于NIO transferTo的实现方式,在Linux和UNIX上,则会使用到零拷贝技术
数据传输并不需要用户态参与,省去了上下文切换和不必要的内拷过程
读取磁盘文件:进行Socket发送,享受这种机制带来的性能和扩展性提高

JAVA IO/NIO源码结构
第三种方式:java标准库提供了文件拷贝方法(java.nio.file.copy)

public static Path copy(Path source, Path target, CopyOption... options)
    throws IOException
public static long copy(InputStream in, Path target, CopyOption... options)
    throws IOException
public static long copy(Path source, OutputStream out) 
throws IOException

这个常见的copy并不是利用transferTo,而是本地技术实现的用户态拷贝(只是简单地用户态空间拷贝)

提高类似拷贝等IO操作的性能:

  • 使用缓存等机制,合理减少IO次数(tcp传输)
  • 使用Transfer To机制,减少上下文切换和额外IO操作
  • 尽量减少不必要的转换过程,比如编解码,对象序列化和发序列化。如:操作文本文件或者网络通信,如果过程不是需要文本信息,可以考虑不要将二进制信息转换成字符串,直接二进制传输

掌握NIO Buffer
Buffer 是NIO操作数据的基本工具,Java为每种原始数据类型提供相应的Buffer实现(布尔除外)
在这里插入图片描述

capacity,它反映了Buffer到底有多大,数组的长度
position,要操作的数据起始位置
limit,相当于操作的限额。
读取操作时,limit设置为所容纳数据的上限
写入时,则会设置容量或者容量以下的可写限度。
Mark,记录上一次position的位置,默认是0,算是便利性考虑
io操作是一个闭环
实例:
1.我们创建一个ByteBuffer,准备写入数据
capacity当然就是缓冲区大小,而position是0
limit默认就是capacity的大小
2.当我们写入几个字节的数据时,position就会跟着动,但是不会超过limit

int bytesRead =inChannel.read(buf);//read into buffer
buf.put(127)

3.我们现在想把写入的数据读出来,需要调用flip方法,将position设置为0,limit一样

//read from buffer into channel.
int bytesWritten = inChannel.write(buf);
byte aByte = buf.get(); 

4.如果还想从头再读一遍,将position置为0

  • Direct Buffer
    定义了一个isDirect()方法,返回当前Buffer是否是Direct类型
    这是因为提供了堆内和堆外两种Buffer,我们以它的allocate或者allocateDirect方法直接创建
  • MappedByteBuffer:将文件按照指定大小直接映射为内存区域,当程序访问这个内存区域时将直接操作这块儿文件数据,省去内核空间向用户空间传输的损耗。我们可以用FileChannel.map创建MappedByteBuffer

在实际使用中,java会尽量对Direct Buffer仅做本地IO操作,对于很多大数据量的IO密集型操作,会到来很大的性能优势:

  • Direct Buffer生命周期内内存地址不会再发生改变,进而内核可以安全的对其进行访问,很多IO操作会很高效
  • 减少了堆内对象存储的可能额外维护工作,所以访问效率可能有所提高

Direct Buffer 我们需要清楚它对内存和JVM参数的影响。
大多数GC中,都不会主动收集,就是基于幻象引用机制,本身并不是public类型,内部实现了一个Deallocator负责销毁的逻辑。对它的销毁往往要拖到full GC的时候,所以一定要小心OOM

对于Direct Buffer的回收:

  • 显式的调用System.gc()来强制触发
  • 重复使用Direct Buffer

跟踪和诊断Direct Buffer内存占用

  • jdk8以后我们可以用 Native Memory Tracking(NMT)特性来进行诊断,你可以在程序启东市加下列参数:
-xx:NativeMemoryTracking={summary|ddetail}

注:激活NMT通常会导致JVM出现性能下降5%-10%

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Java中,您可以使用NIO(New I/O)来拷贝文件。NIO提供了更高效的I/O操作方式,特别是在处理大文件时。以下是一个使用NIO拷贝文件的示例代码: ```java import java.io.IOException; import java.io.FileInputStream; import java.io.FileOutputStream; import java.nio.channels.FileChannel; public class FileCopyExample { public static void main(String[] args) { String sourceFile = "path/to/source/file.txt"; // 源文件路径 String destinationFile = "path/to/destination/file.txt"; // 目标文件路径 try { // 创建输入流和输出流 FileInputStream fis = new FileInputStream(sourceFile); FileOutputStream fos = new FileOutputStream(destinationFile); // 获取输入流和输出流的通道 FileChannel sourceChannel = fis.getChannel(); FileChannel destinationChannel = fos.getChannel(); // 使用 transferTo() 方法拷贝文件 destinationChannel.transferFrom(sourceChannel, 0, sourceChannel.size()); // 关闭通道和流 sourceChannel.close(); destinationChannel.close(); fis.close(); fos.close(); System.out.println("文件拷贝完成"); } catch (IOException e) { e.printStackTrace(); } } } ``` 在上述示例中,您需要将 `sourceFile` 和 `destinationFile` 的值替换为实际的源文件路径和目标文件路径。代码将打开源文件和目标文件的输入流和输出流,并获取它们的通道。然后,通过调用 `transferFrom()` 方法来拷贝文件数据。最后,关闭通道和流。 请注意,以上代码只是一个简单的示例,没有处理异常情况和错误处理。在实际的应用中,您可能需要添加适当的异常处理和错误检查。 另外,还有其他一些方法可以使用NIO拷贝文件,例如使用 `transferTo()` 方法、使用 `read()` 和 `write()` 方法逐个字节拷贝等。您可以根据自己的需求选择适合的方法。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值