FileChannel与ByteBuffer的使用示例

FileChannel的作用:

channel可以只操作一个文件的一部分,chunk操作, 比如替换大文件的文件头
文件截断truncate
IO性能

ByteBuffer

HeapByteBuffer = ByteBuffer.allocate(n)申请堆上的buffer
DirectByteBuffer = ByteBuffer.allocateDirect(n)申请系统直接内存,使用场景和作用

生命周期长的大对象,

减少java堆GC, 减少内存copy

http://www.importnew.com/26334.html

Buffer.flip()干啥用的?

buffer.put(xxx)之后, buffer的position会指到当前xxx的size,如果想进行写操作,需要把position重新指到0.

String file = "xxx.txt"; //abcdef
        byte[] bb = new byte[4];
        bb[0] = 0b1111;
        bb[1] = 0b1111;
        bb[2] = 0b1111;
        bb[3] = 0b1111;
        try (RandomAccessFile writer = new RandomAccessFile(file, "rw");
             FileChannel channel = writer.getChannel()) {
//            ByteBuffer buff = ByteBuffer.wrap(new String(bb).getBytes());
            ByteBuffer buff = ByteBuffer.allocate(6);
            buff.put(bb);  // java.nio.HeapByteBuffer[pos=0 lim=6 cap=6] ==> java.nio.HeapByteBuffer[pos=4 lim=6 cap=6]
            buff.flip();  // java.nio.HeapByteBuffer[pos=4 lim=6 cap=6]  ==> java.nio.HeapByteBuffer[pos=0 lim=4 cap=6]
            channel.write(buff);
            channel.force(true);
        }

如果不加flip(), pos到lim之间的数据4-6会写到文件(pos=lim时,nothing is write to file).
channel.write(buff)只写pos到lim之间的数据。
只替换指定大小的文件部分,剩余部分文件内容不变

Buffer.compact()干啥用的?

字面意思是·压缩·,把pos到limit的数据copy到从0开始的位置,然后pos变成数据大小+1

这个方法是用在write()之后的,不是用在write前面

文件copy的性能

public class DirectByteBufferTest {


    @Test
    public void test_copyFile() throws IOException {
        ByteBuffer byteBuffer = ByteBuffer.allocateDirect(10);//100kbytes
        FileChannel readChannel = FileChannel.open(new File("D:/in.txt").toPath());
        //out.txt必须已经存在, writeChannel必须以WRITE方式打开
        FileChannel writeChannel = FileChannel.open(new File("D:/out.txt").toPath(), StandardOpenOption.WRITE);
        int read;
        while ((read = readChannel.read(byteBuffer)) != -1) {
            //buffer从读切换到写
            byteBuffer.flip();
            // 打印信息必须放在flip后面, 否则decode出来的是上次read的结果.根据in.txt的字符编码修改下面的ISO_8859_1
            //System.out.println(read + "--" + StandardCharsets.ISO_8859_1.decode(byteBuffer));
            writeChannel.write(byteBuffer);
            // 写完之后清空缓冲区,否则read=0一直死循环
            byteBuffer.clear();
        }
        writeChannel.close();
        readChannel.close();
    }
}

其他总结:

ByteBuffer.allocateDirect缓冲区大小根据输入文件的大小调整,但是太大时输出性能也提高不了多少, 对于大文件,1M的缓存区应该差不多了。
对于超大文件, 应该换成其他读取方式

benchmark: https://developer.ibm.com/articles/j-zerocopy

posted @ 2018-08-01 16:32  funny_coding  阅读(1060)  评论(0编辑  收藏  举报
build beautiful things, share happiness