【问题标题】:Why does Java memory mapped buffer cause massive unexpected disk IO?为什么 Java 内存映射缓冲区会导致大量意外磁盘 IO?
【发布时间】:2014-03-11 23:13:26
【问题描述】:

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

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

在完全相同的系统上,我用 Java 编写了在 Oracle JDK 7 上运行的等效程序,并注意到在整个程序执行过程中存在大量磁盘 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);
}
}

【问题讨论】:

  • 这可能与您的编码方式有关。不看代码,是无法分辨的。
  • @NPE 如果您密切关注您的任何 mmap 程序,这是一个明显的现象。但我还是把代码放了。
  • 您可以改写 long,但您只需要触摸每一页(每 4 KB)即可更快地弄脏数据。请参阅 MappedByteBuffer.load() 的实现,它所做的只是每 4 KB 读取一次。
  • 你的 JVM 有多少内存用于内存映射?
  • @user2357112 JVM 不限制内存映射的使用。您可以将所有内存限制设置为几 MB,但仍会映射到 TB 的内存。

标签: java linux performance memory file-io


【解决方案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 并对其执行线程安全操作。例如您可以跨进程共享内存。它支持堆外锁、易失性读/写、有序写和CAS。

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

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

【讨论】:

  • @Howard 在一个地方看到 Java 所做的事情的本质也很有用,而不是分散在许多不在 src.zip 中的类中
  • @Howard 您可以从 MemoryStore 创建字节切片并对其进行序列化/反序列化,例如作为 ObjectInput/Output,或将数据结构映射到它。
  • 我正在 MemoryStore 之上构建一个 SharedHashMap(即完全在堆外),它是并发的,在进程之间共享,并且可以通过 TCP 进行复制。
猜你喜欢
  • 2011-04-22
  • 1970-01-01
  • 2015-08-11
  • 2012-06-16
  • 1970-01-01
  • 2016-12-21
  • 2011-04-20
  • 2019-10-26
  • 1970-01-01
相关资源
最近更新 更多