FileChannel和MMAP的使用以及benchmark

前言

最近弄了一些小东西,发现它们都有个共同点:对文件的操作。无论是文件的快速写入又或者是大文件的读取等,都要求我们对文件IO有很深的了解。不同文件大小的读写操作,应该选择哪种方式能达到最快的效果?接下来我们看看Java中常见的文件操作方式以及它们在读写大文件时各自的效果。



FileChannel与MMAP

FileChannel(文件管道):就是连接文件的一个管道,与MMap同属于java.nio下,这不意味着他是非阻塞的,它总是以阻塞的方式运行。

MMap(内存映射):将文件映射到用户空间的虚拟内存中,不用再从内核缓冲区到用户空间了,使得我文件的中的位置再虚拟内存中有了对应的地址,可以像操作内存一样操作这个文件,相当于已经把整个文件放入内存,但在真正使用到这些数据前却不会消耗物理内存,也不会有读写磁盘的操作,只有真正使用这些数据时,也就是图像准备渲染在屏幕上时,虚拟内存管理系统 VMS 才根据缺页加载的机制从磁盘加载对应的数据块到物理内存进行渲染。这样的文件读写文件方式少了数据从内核缓存到用户空间的拷贝,效率很高。

1
2
3
4
// FileChannel
FileChannel fc = new RandomAccessFile(file, "rw").getChannel();
// MMap
MappedByteBuffer mb = fc.map(FileChannel.MapMode.READ_WRITE, 0, size);

我们可以看到,mmap其实就是FileChannel衍生出来的一种方式,通过FileChannel的map()方法获取。

FileChannel

write

1
2
3
4
5
6
byte[] buf = new byte[1024];
long position = 10L;
// 获取FileChannel
FileChannel fc = new RandomAccessFile(file, "rw").getChannel();
// 从指定位置开始写入数据
fc.write(ByteBuffer.wrap(buf), position);

read

1
2
3
4
ByteBuffer buf = ByteBuffer.allocate(1024);
FileChannel fc = new RandomAccessFile(fFile, "rw").getChannel();
// 从当前位置将数据读取到ByteBuffer中,如果读取完了l为-1
int l = fc.read(buf);

MMAP

write

1
2
3
4
5
6
7
// 通过FileChannel的map()方法获取MappedByteBuffer对象
MappedByteBuffer mb = new RandomAccessFile(file, "rw")
.getChannel().map(FileChannel.MapMode.READ_WRITE, 0, size);
byte[] buf = new byte[1024];
Arrays.fill(buf, (byte) 1)
// 将数据写入文件中
mb.put(buf);

read

1
2
3
4
5
MappedByteBuffer mb = new RandomAccessFile(file, "rw")
.getChannel().map(FileChannel.MapMode.READ_WRITE, 0, size);
byte[] buf = byte[1024];
// 将数据读取到buf中
mb.get(buf);

Benchmark

接下来我们看看FileChannel与MMap在对1GB文件读写时不同数据大小的表现。

代码地址

测试环境

CPU:4核

内存:8G

磁盘:50G / 高性能云盘

最大IOPS:6000


1G文件写

我们可以看到,在1G数据写入情况下,只有在512字节的时候MMap表现的更好,当到1KB以上时与FileChannel不相上下甚至超过FileChannel。所以在小于1KB的数据包写入时建议使用MMap,超过1KB的使用FileChannel


1G文件读

在读1G文件的情况下,当数据包大小在4KB及以下时,MMap可以带来更好的读取速度,而当超过4KB的数据包时,MMap的效果就不太明显了,反倒是FileChannel的速度比MMap的快。所以在数据包在4KB及以下的情况下,使用MMap的效果优于FileChannel,当数据包大于4KB时,使用FileChannel的效果更好。


作者

zhaommmmomo

发布于

2022-01-13

更新于

2023-06-27

许可协议