到 allocate()还是到 allocateDirect(),这是个问题。
allocate()
allocateDirect()
多年来,我一直坚持这样的想法,既然 DirectByteBuffer是操作系统级别的直接内存映射,那么它在 get/put 调用方面会比 HeapByteBuffer执行得更快。直到现在,我才真正感兴趣了解有关情况的确切细节。我想知道这两种 ByteBuffer中哪一种更快,在什么条件下更快。
DirectByteBuffer
HeapByteBuffer
ByteBuffer
最好自己量尺寸。快速答案似乎是,从 allocateDirect()缓冲区发送比 allocate()变体(通过将文件复制到/dev/null 进行测试)节省25% 到75% 的时间,这取决于大小,但是分配本身可以比 意义重大慢(甚至是100倍)。
资料来源:
为什么 ByteBuffer.distribution ()和 ByteBuffer.allocateDirect ()之间存在奇数的性能曲线差异
分配直接慢得可笑
何时使用 Array、 Buffer 或 direct Buffer
没有理由期望直接缓冲区能够更快地访问 在里面和 jvm。当您将它们传递给本机代码时,它们的优势就出现了——例如,各种通道后面的代码。
Ron Hitches 在他的优秀著作 Java NIO中似乎提供了一个我认为可以很好地回答你的问题的答案:
操作系统执行 I/O 对记忆区域的操作。这些 内存区域,至于操作 系统是相关的,是连续的 字节序列,不足为奇 那么只有字节缓冲区是 有资格参与输入/输出 此外,亦记得 操作系统将直接访问 进程的地址空间,在 这种情况下的 JVM 进程,转移 这意味着内存区域 是 I/O 操作的目标必须 是连续的字节序列 在 JVM 中,字节数组可能不是 连续存储在内存中,或者 垃圾收集者可以在任何地方移动它 数组是 Java 中的对象,而 存储数据的方式 对象可能不同于一个 JVM 实施。 由于这个原因,一个 引入直接缓冲 缓冲区用于交互 通道和本地 I/O 例程。 他们尽最大努力储存 内存区域中的字节元素 通道可用于直接,或原始, 通过使用本机代码来告诉 要排水或填充的操作系统 直接进入记忆区。 直接字节缓冲区通常是 I/O 操作的最佳选择 设计,他们支持最 有效的 I/O 机制 非直接字节缓冲区可以是 传递到频道,但这样做可能 会被处以表演罚款 通常不可能是非直接的 缓冲区作为本机的目标 输入输出操作。如果您通过非直接 字节缓冲区对象的信道 写入时,通道可以隐式地做 每通电话的内容如下: 创建一个临时的直接 ByteBuffer 对象。 复制非直接 到临时缓冲区的缓冲区。 执行低级 I/O 操作 使用临时缓冲区。 临时缓冲区对象将出局 范围,并最终成为垃圾 收集。 这可能导致缓冲区 复制和对象在每个输入输出, 这些正是 我们希望避免。然而,取决于 在实施方面,情况未必如此 运行时可能会出现这种情况 缓存和重用直接缓冲区或 表演其他巧妙的技巧来提高 如果只是简单地创建 一次性使用的缓冲区 差异不显著。在 另一方面,如果您将使用 缓冲重复在一个 高性能的情况下,你是 最好分配直接缓冲区 然后重复使用。 直接缓冲区是 I/O 的最佳选择, 但它们可能更昂贵 创建比非直接字节缓冲区。 直接缓冲区使用的内存是 通过调用 本地的,特定于操作系统的 代码,绕过标准的 JVM 堆。 直接建立和拆除 缓冲区可能要大得多 比堆驻留缓冲区更昂贵, 取决于主机操作系统 和 JVM 实现 直接缓冲区的存储区 不受垃圾回收的约束 因为它们超出了标准 JVM 堆。 使用 直接缓冲区和非直接缓冲区可以 JVM、操作系统、, 和代码设计。通过分配内存 在堆外面,你可以把你的 对附加力量的适用 而 JVM 并不知道 把额外的运动部件带入 玩,确保你有所成就 预期效果。我建议 古老的软件格言: 首先做到 工作,那就快点,别担心 预先进行了太多的优化; 首先集中精力在正确性上 JVM 实现可以 执行缓冲区缓存或其他 这些优化将为您提供 你需要的表现 你做了不必要的努力。
操作系统执行 I/O 对记忆区域的操作。这些 内存区域,至于操作 系统是相关的,是连续的 字节序列,不足为奇 那么只有字节缓冲区是 有资格参与输入/输出 此外,亦记得 操作系统将直接访问 进程的地址空间,在 这种情况下的 JVM 进程,转移 这意味着内存区域 是 I/O 操作的目标必须 是连续的字节序列 在 JVM 中,字节数组可能不是 连续存储在内存中,或者 垃圾收集者可以在任何地方移动它 数组是 Java 中的对象,而 存储数据的方式 对象可能不同于一个 JVM 实施。
由于这个原因,一个 引入直接缓冲 缓冲区用于交互 通道和本地 I/O 例程。 他们尽最大努力储存 内存区域中的字节元素 通道可用于直接,或原始, 通过使用本机代码来告诉 要排水或填充的操作系统 直接进入记忆区。
直接字节缓冲区通常是 I/O 操作的最佳选择 设计,他们支持最 有效的 I/O 机制 非直接字节缓冲区可以是 传递到频道,但这样做可能 会被处以表演罚款 通常不可能是非直接的 缓冲区作为本机的目标 输入输出操作。如果您通过非直接 字节缓冲区对象的信道 写入时,通道可以隐式地做 每通电话的内容如下:
这可能导致缓冲区 复制和对象在每个输入输出, 这些正是 我们希望避免。然而,取决于 在实施方面,情况未必如此 运行时可能会出现这种情况 缓存和重用直接缓冲区或 表演其他巧妙的技巧来提高 如果只是简单地创建 一次性使用的缓冲区 差异不显著。在 另一方面,如果您将使用 缓冲重复在一个 高性能的情况下,你是 最好分配直接缓冲区 然后重复使用。
直接缓冲区是 I/O 的最佳选择, 但它们可能更昂贵 创建比非直接字节缓冲区。 直接缓冲区使用的内存是 通过调用 本地的,特定于操作系统的 代码,绕过标准的 JVM 堆。 直接建立和拆除 缓冲区可能要大得多 比堆驻留缓冲区更昂贵, 取决于主机操作系统 和 JVM 实现 直接缓冲区的存储区 不受垃圾回收的约束 因为它们超出了标准 JVM 堆。
使用 直接缓冲区和非直接缓冲区可以 JVM、操作系统、, 和代码设计。通过分配内存 在堆外面,你可以把你的 对附加力量的适用 而 JVM 并不知道 把额外的运动部件带入 玩,确保你有所成就 预期效果。我建议 古老的软件格言: 首先做到 工作,那就快点,别担心 预先进行了太多的优化; 首先集中精力在正确性上 JVM 实现可以 执行缓冲区缓存或其他 这些优化将为您提供 你需要的表现 你做了不必要的努力。
因为 DirectByteBuffers 是直接的 操作系统级内存映射
他们不是。它们只是普通的应用程序进程内存,但在 JavaGC 期间不受重定位的影响,这极大地简化了 JNI 层内部的工作。你所描述的适用于 MappedByteBuffer。
MappedByteBuffer
使用 get/put 调用会执行得更快
结论不是从前提出来的,前提是假的,结论也是假的。一旦进入 JNI 层,它们就会更快,如果从相同的 DirectByteBuffer读写,它们就会更快,因为数据根本不需要跨越 JNI 边界。