【问题标题】:Memory-mapped files in JavaJava 中的内存映射文件
【发布时间】:2010-11-04 14:58:48
【问题描述】:

我一直在尝试编写一些必须执行大量 I/O 的非常快速的 Java 代码。我正在使用一个返回 ByteBuffer 的内存映射文件:

public static ByteBuffer byteBufferForFile(String fname){
    FileChannel vectorChannel;
    ByteBuffer vector;
    try {
        vectorChannel = new FileInputStream(fname).getChannel();
    } catch (FileNotFoundException e1) {
        e1.printStackTrace();
        return null;
    }
    try {
        vector = vectorChannel.map(MapMode.READ_ONLY,0,vectorChannel.size());
    } catch (IOException e) {
        e.printStackTrace();
        return null;
    }
    return vector;
}

我遇到的问题是 ByteBuffer .array() 方法(应该返回一个 byte[] 数组)不适用于只读文件。我想编写我的代码,以便它可以与在内存中构造的内存缓冲区和从磁盘读取的缓冲区一起使用。但我不想将我所有的缓冲区都包装成一个 ByteBuffer.wrap() 函数,因为我担心这会减慢速度。所以我一直在写所有东西的两个版本,一个需要一个 byte[],另一个需要一个 ByteBuffer。

我应该把所有东西都包起来吗?还是我应该把所有东西都写一遍?

【问题讨论】:

  • 基准测试并发布结果,即使专家可以为您指出一个方向,该方向可能不适用于您的情况。基准!!!!

标签: java memory-mapping


【解决方案1】:

是否有人真正检查过内存映射创建的ByteBuffers 是否支持首先调用.array(),而不管只读/读写?

据我所知,答案是NOByteBuffer 通过ByteBuffer.array() 返回直接byte[] 数组的能力受ByteBuffer.hb (byte[]) 的存在的约束,在创建MappedByteBuffer 时始终设置为null。

这对我来说有点糟糕,因为我希望做一些类似于问题作者想做的事情。

【讨论】:

  • 我同意。糟透了。我不敢相信 ByteBuffer 没有实现 array()。另一方面,我们做了一些性能测试,我们发现使用 .get() 和内存映射文件有时比使用编程的 io 更快,有时使用编程的 io 更快。这很奇怪。但是编程 io 的差异比内存映射文件的差异更大。
  • A byte[] 必须在堆上。内存映射的内存块必须在堆之外。如果区别是透明的就好了,但无论如何我更喜欢使用 ByteBuffer 的 getLong/putLong 方法(使用本机排序会更快)
【解决方案2】:

不要重新发明轮子总是好的。 Apache 提供了一个漂亮的库来执行 I/O 操作。看看http://commons.apache.org/io/description.html

这是它所服务的场景。假设您有一些数据 更喜欢保存在内存中,但你不提前知道有多少数据 那里将是。如果太多,您想将其写入磁盘 而不是占用内存,但你不想写入磁盘,直到你 需要,因为磁盘很慢并且是需要跟踪的资源 清理。

因此,您创建了一个临时缓冲区并开始写入该缓冲区。如果/当你 达到您想要保留在内存中的内容的阈值,您需要 创建一个文件,将缓冲区中的内容写入该文件,然后将所有内容写入 后续数据到文件而不是缓冲区。

这就是 DeferredOutputStream 为您所做的。它隐藏了所有的混乱 在切换点附近。您需要做的就是创建 首先是延迟流,配置阈值,然后就 随心所欲地写。

编辑:我刚刚用谷歌做了一个小的重新搜索,发现了这个链接: http://lists.apple.com/archives/java-dev/2004/Apr/msg00086.html (闪电般的文件读/写)。非常令人印象深刻。

【讨论】:

  • 如果我错了,请纠正我。您正在寻找进行 I/O 操作的快速方法。对吗??
  • 实际上,我只是在寻找快速的方法,但我也在寻找以最少的缓冲区副本处理缓冲区的方法。
  • @GauravSaini:你是指来自 Apache commons-io 的 DeferredOutputStream 吗?我在 v2.3 和 v2.2 的 Javadoc 中找不到这样的类。
【解决方案3】:

包装 byte[] 不会减慢速度……不会有任何巨大的数组副本或其他小的性能问题。来自 JavaDocs:java.nio.ByteBuffer .wrap()

将字节数组包装到缓冲区中。

新缓冲区将由给定的字节数组支持;即, 对缓冲区的修改将导致 要修改的数组,反之亦然 反之亦然。新缓冲区的容量和 限制将是array.length,它的 位置将为零,其标记 将是未定义的。它的后备阵列 将是给定的数组,它的数组 偏移量为零。

【讨论】:

  • 谢谢。我只是担心必须使用 .get(i) 而不是 [i] 读取每个字节,因为 .get(i) 涉及方法调用,而 [i] 是在字节码中完成的。
  • 这似乎是一个非常“细粒度”的性能问题,对我来说就像过早的优化。 JVM 很擅长这样的事情。对其进行基准测试,以一种或另一种方式向自己证明。
  • 其实,我在做计算机取证,处理 TB 级的信息。以我迄今为止的经验来看,JVM 并没有像我希望的那样优化。
  • 你有没有因为上面的方法调用而显示性能损失的基准?我无法想象它只是次要的,并且会认为还有其他更适合调整的领域。对这些事情采取科学的方法胜过一切。嗯......可能会在周末调查只是为了好玩! :)
  • 另外,我要问一下,您使用的是什么版本的 Java?每个主要版本的 JVM 性能都有相当大的改进。如果您的任务对性能很敏感,那么您应该使用 Java 6。
【解决方案4】:

使用 ByteBuffer.wrap() 功能不会带来很大的负担。它分配一个简单的对象并初始化几个整数。因此,如果您需要处理只读文件,那么针对 ByteBuffer 编写算法是您的最佳选择。

【讨论】:

    猜你喜欢
    • 2011-05-14
    • 2014-04-04
    • 1970-01-01
    • 2011-02-04
    • 2014-11-16
    • 1970-01-01
    • 2011-02-27
    • 2015-09-07
    • 2016-02-23
    相关资源
    最近更新 更多