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

标签 java linux performance memory file-io

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

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

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

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

操作系统是 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);
}
}

最佳答案

内存映射完全在操作系统中实现。 JVM 对如何将其刷新到磁盘没有发言权,除非在您选择文件时通过 force() 方法和 "rws" 选项。

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% 的 writeback 意味着写程序将停止,直到他的脏百分比下降到 20% 以下。无论如何,数据将在 3000 厘秒或 30 秒后写入磁盘。


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


顺便说一句,您可能会觉得这门课很有趣。 MemoryStore允许您映射任何大小的内存,即 >> 2 GB 并对其执行线程安全操作。例如您可以跨进程共享内存。它支持堆外锁、 volatile 读/写、有序写和CAS。

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

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

关于java - 为什么 Java 内存映射缓冲区会导致大量意外磁盘 IO?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21748078/

相关文章:

java - 安卓工作室 : Change EditText filed from a java class

ruby-on-rails - Ruby - 在 tmp/pids/thin.pid (Thin::PidFileNotFound) 中找不到 PID

Java 应用程序在一个系统上运行缓慢,但在另一个系统上运行却非常快?

silverlight - 性能分析 Windows Phone 7 应用程序 (SL/XNA)

java - 应用程序启动时在 Android 中_快速_加载(反序列化)2MB 数据

java - 是否有与 Python 3 的 collections.Counter 等效的 scala/java

java - 在不增加堆大小的情况下修复超出 GC 开销限制的问题

java - 环境变量中的数据库名称

linux - bash 中的脚本错误

linux - 使用 sed 运行 Linux find 命令时出错