为什么Java内存映射缓冲区会导致大量意外的磁盘IO?

8 java linux memory performance file-io

我编写了一些使用映射文件缓冲区的Posix程序.一个简单的场景是将1GB文件映射到内存中,并用内容填充整个文件.

在程序执行期间,直到msyncmunmap调用发生时几乎没有磁盘IO .

在完全相同的系统上,我写下了在Oracle JDK 7上运行的Java中的等效程序,并注意到整个程序执行过程中的大量磁盘IO活动.

内存映射文件缓冲区在JVM中的实现方式有何不同?反正有没有推迟大规模的IO活动?

操作系统是Linux 3.2 x64.

码:

import java.io.RandomAccessFile;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;

public class Main {
public static void main(String[] args) throws Exception {
    long size = 1024 * 1048576;
    RandomAccessFile raf= new RandomAccessFile("mmap1g", "rw");
    FileChannel fc = raf.getChannel();
    MappedByteBuffer buf = fc.map(FileChannel.MapMode.READ_WRITE, 0, size);
    for(long i = 0; i < size; ++i)
        buf.put((byte)1);
}
}
Run Code Online (Sandbox Code Playgroud)

Pet*_*rey 8

内存映射完全在操作系统中实现.除了通过选择文件时的force()方法和"rws"选项之外,JVM没有说明如何将其刷新到磁盘.

Linux将根据sysctl中设置的内核参数刷新到磁盘.

$ sysctl -a | grep dirty
vm.dirty_background_bytes = 0
vm.dirty_background_ratio = 10
vm.dirty_bytes = 0
vm.dirty_expire_centisecs = 3000
vm.dirty_ratio = 20
vm.dirty_writeback_centisecs = 500
Run Code Online (Sandbox Code Playgroud)

这些是我的笔记本电脑上的默认设置.该比率10意味着当10%的主内存变脏时,它将开始在后台将数据写入磁盘.写回20%意味着写作程序将停止,直到他的脏百分比降至20%以下.无论如何,数据将在3000厘秒或30秒后写入磁盘.


一个有趣的比较,它对内存映射文件tmpfs系统上的文件.我已/tmp安装为tmpfs但大多数系统都有/ dev/shm.


顺便说一句你可能会发现这堂课很有趣. MemoryStore允许您映射任何大小的内存,即>> 2 GB并对其执行线程安全操作.例如,您可以跨进程共享内存.它支持关闭堆锁,易失性读/写,有序写和CAS.

我有一个测试,其中两个进程锁定,切换,解锁记录,我的笔记本电脑平均延迟为50 ns.

BTW2:Linux具有稀疏文件,这意味着您可以映射不仅大于主内存但大于可用磁盘空间的区域.例如,如果你映射到8 TB并且只使用4 GB的随机片段,它将在内存中使用最多4 GB,在磁盘上使用4 GB.如果使用,du {file}您可以看到实际使用的空间.注意:延迟分配磁盘空间可能会导致高度碎片化的文件,这可能是HDD的性能问题.