12 java的文件拷贝方式及NIO相关知识扩展

java的文件拷贝方式及NIO相关知识扩展:

java的文件拷贝方式:

  1. 利于FileInputStream读取,利用FileOutputStream写入;代码如下:(此处buffer与Buffered stream效果一致,取其一即可,无需两者同时使用,建议使用buffered stream)

       public static void copy(String source,String target) throws IOException {
         try(FileInputStream input = new FileInputStream(source);
             FileOutputStream out = new FileOutputStream(target)){
           BufferedOutputStream bufferedOut = new BufferedOutputStream(out);
           BufferedInputStream bufferedInput = new BufferedInputStream(input);
           byte[] buffer = new byte[10240];
           int size = 0;
           while((size = bufferedInput.read(buffer)) > 0) {
             bufferedOut.write(buffer,0,size);
           }
           bufferedOut.flush();
         }
       }
    
  2. 利用java.nio提供的trasferTo或者trasferFrom方法实现,它更能利用现代操作系统的底层机制,避免不必要的拷贝和上下文切换,代码如下:

       public static void copy2(String source,String target) throws IOException {
         try(FileChannel input = new FileInputStream(source).getChannel();
             FileChannel out = new FileOutputStream(target).getChannel();){
           long length = input.size();
           //while( length > 0) {
           //length -= input.transferTo(input.position(),length,out);
           //}
           //or
           while(length > 0) {
             //transferFrom并不会改变out流的position位置,
             //但是会改变的input流的position位置;
             length -= out.transferFrom(input,input.position(),length);
           }
         }
       }
    

为什么transferTo或者transferFrom有性能优势:

  1. 理解内核态空间和用户态空间:

    1. 普通应用程序运行于用户态空间,权限受限,无法对底层IO进行操作,相关的操作需要切换到内核态空间才能执行;这就会带来空间切换的开销;系统内核、硬件驱动都运行于内核态空间,具有较高的权限;
  1. 用户态空间下进行文件拷贝示意图:
用户态空间文件拷贝示意图.png
  1. 用户态空间下文件拷贝实际上进行了多次上下文切换,且数据需要先从磁盘读取到内核缓存,在切换到用户态将数据从内核缓存读取到用户缓存;写入操作也是类似,这就带来了额外开销,降低IO效率;
  1. transferTo或者transferFrom则能利用底层操作系统机制,在Linux上使用零拷贝技术,数据传输无需用户态参与,省去了上下文切换的开销与不必要的内存拷贝;示意图如下:
    transferTo文件拷贝示意图.png

标准库(JDK1.8u201)提供的相关拷贝(java.nio.file.Files.copy):

  1. public static Path copy(Path source, Path target, CopyOption... options)throws IOException

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

  3. public static long copy(Path source, OutputStream out) throws IOException {

以上方法均使用的是用户态空间拷贝;其中第一个方法是使用本地技术实现的用户态拷贝;1.8以后的JDK版本下面两个函数版本可能会采用transferTo或者transferFrom进行优化;

提高文件拷贝性能的原则:

  1. 尽量使用缓存,合理减少IO访问次数;
  2. 使用transferTo机制,减少上下文切换和不必要内存拷贝;
  3. 减少不必要的转换,直接传输二进制;如:编解码、序列化和反序列化;

掌握NIO Buffer;

1. buffer属性:

  1. capacity,它反映这个buffer的大小;
  2. limit:它反映这个buffer的可读或者可写限度;
  3. position:该buffer目前读取、写入的位置;都是从0开始的;
  4. mark:便利性的考虑,可以通过mark()记录地址,reset将position设置为上一次mark的位置;clear,flip,rewind都会清零mark(设置为-1);

2. buffer注意:

  1. 即使调用了flip方法,也需要注意的是该buffer依然能够写入数据,并且position会随着写入字节水涨船高,如果超过limit大小,则会抛出BufferOverflowException;
  2. buffer.get()方法可以传入一个byte数组,注意,该方法会根据byte数组的大小来决定读取多少,使用get方法从0开始填充到数组的最后一位,如果byte大小超过了ByteBuffer的可以读取的限度,则会抛出BufferUnderflowException;

3. buffer的方法:

  1. put(),get():基本的写入和读取方法,会影响position的数值,还有putInt,putFloat等版本,int占4个byte空间,如果是ByteBuffer则position会增长4;
  2. flip方法:将position设置为0,limit设置为当前的position那里,并清除mark;
  3. rewind:position设置为0,请出mark;
  4. clear:position设置为0,limit设置为capacity,清除mark;
  5. hasRemaining():返回当前position和limit之间是否还有元素;
  6. Remaining():返回还有多少元素;
  7. reset():position设置为mark的位置;

4. direct buffer、MappedByteBuffer和垃圾收集

  1. MappedByteBuffer:FileChannel.map()可以创建,本质上也是一种DirectBuffer,它将文件按照指定大小直接映射为内存区域,当程序访问内存区域时,将直接操作文件数据,省去了数据从内核空间向用户空间传输的损耗,使用写模式时注意使用RandomAccessFile与其配合,因为无论是FileInputStream还是FileOutputStream均不能满足其map中的读写模式,但又没有提供只写模式,但其也存在一些问题:如内存占用、文件关闭不确定,被其打开的文件只有在垃圾回收的才会被关闭,而且这个时间点是不确定的;

  2. 获取direct buffer:ByteBuffer.allocateDirect(1000)来获取DirectBuffer;

  3. 检测是否是DirectBuffer:buffer提供了isDirect函数来检测是否是DirectBuffer

  4. Direct Buffer 在实际使用过程中,java会尽量对其仅作本地IO操作,对于很多大数据量的IO操作,会带来非常大的性能优势:

    1. 因为生命周期内地址都不会再改变,内核可以安全对其进行访问,所以很多IO操作都会很高效;
    2. 因为是堆外对象,省去了额外的维护开销,访问效率更高;
  5. Direct Buffer使用注意:

    1. 创建和销毁都比一般buffer开销要大;

    2. -Xmx不能影响其使用,因为其是堆外存储;限制方式如下:
      -XX:MaxDirectMemorySize=512M

      所以在使用DirectBuffer时,应注意内存大小需要考虑其空间占用;否则容易导致OOM;

    3. 大部分gc不会主动收集DirectBuffer;它的销毁往往要等到FullGC;

  6. 使用建议:

    1. 重复使用DirectBuffer;
    2. 跟踪和诊断DirectBuffer内存占用:
      1. -XX:NativeMemoryTracking={summary|detail}可以开启Native Memory Tracking(NMT)特性,激活 NMT 通常都会导致 JVM 出现 5%~10% 的性能下降,采用以下命令进行交互式对比:

         // 打印NMT信息
         jcmd <pid> VM.native_memory detail 
         
         // 进行baseline,以对比分配内存变化
         jcmd <pid> VM.native_memory baseline
         
         // 进行baseline,以对比分配内存变化
         jcmd <pid> VM.native_memory detail.diff
        

Scattering Reads And Gathering Writes

定义:

Scattering Reads

Scattering Reads是指数据从一个channel读取到多个buffer中。如下图描述:


scatteringRead.png

Gathering Writes

Gathering Writes是指数据从多个buffer写入到同一个channel。如下图描述:


gatherWrite.png

使用语法:

ByteBuffer header = ByteBuffer.allocate(128);  
ByteBuffer body = ByteBuffer.allocate(1024);  
ByteBuffer[] dataBuffers = {header, body};
//注意只会将buffer中的有效数据写入或者读取channel,也就是说受limit的影响,
//如果channel中的数据大于buffer总空间,不会抛出异常,依次读取写入buffer,能读多少读多少;
channle.write(dataBuffers);
channel.read(resultBuffers);  
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,657评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,662评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,143评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,732评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,837评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,036评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,126评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,868评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,315评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,641评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,773评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,470评论 4 333
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,126评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,859评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,095评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,584评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,676评论 2 351