我编写了一些使用映射文件缓冲区的Posix程序。一个简单的场景是将1GB文件映射到内存中,并用内容填充整个文件。
在程序执行期间,在msync
或munmap
调用发生之前几乎没有磁盘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);
}
}
答案 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的性能问题。