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

时间:2014-02-13 07:45:55

标签: java linux performance memory 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);
}
}

1 个答案:

答案 0 :(得分: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

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


一个有趣的比较,它将内存映射到tmpfs文件系统上的文件。我将/tmp挂载为tmpfs,但大多数系统都有/ dev / shm。


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

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

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