要allocate()
或allocateDirect()
,这就是问题。
多年以来,我一直坚持认为,因为DirectByteBuffer
是OS级别的直接内存映射,所以get / put调用的执行速度比HeapByteBuffer
更快。到目前为止,我从未真正有兴趣了解有关情况的具体细节。我想知道这两种ByteBuffer
中哪一种更快,哪种条件更快。
答案 0 :(得分:141)
操作系统执行I / O. 对记忆区域的操作。这些 记忆区域,就操作而言 系统而言,是连续的 字节序列。这并不奇怪 然后只有字节缓冲区 有资格参加I / O. 操作。还记得那个 操作系统将直接访问 进程的地址空间 这种情况是JVM进程,要转移 数据。这意味着内存区域 这是I / O的目标必须 是连续的字节序列。在 JVM,字节数组可能不是 连续存储在内存中,或者 垃圾收集器可以在任何地方移动它 时间。数组是Java中的对象,和 数据存储在其中的方式 对象可能因JVM而异 实施到另一个。
出于这个原因,a的概念 引入了直接缓冲区。直接 缓冲区用于交互 使用通道和本机I / O例程。 他们尽最大努力存储 a。内存区域中的字节元素 通道可用于直接或原始, 通过使用本机代码来进行访问 操作系统要排空或填充 直接记忆区域。
直接字节缓冲区通常是 I / O操作的最佳选择。通过 设计,他们支持最多 高效的I / O机制 JVM。非直接字节缓冲区可以是 传递到频道,但这样做可能 招致绩效惩罚。它的 通常不可能是非直接的 缓冲区是本机的目标 I / O操作。如果您传递非直接 ByteBuffer对象为一个通道 写,通道可能含蓄地做 每次通话都有以下内容:
- 创建临时直接ByteBuffer 对象。
- 复制非直接内容 缓冲区到临时缓冲区。
- 执行低级I / O操作 使用临时缓冲区。
- 临时缓冲区对象熄灭 范围,最终是垃圾 收集。
醇>这可能会导致缓冲 在每个I / O上复制和对象流失, 这正是各种各样的事情 我们想避免。但是,依赖 关于实施,事情可能没有 这很糟糕。运行时可能会 缓存和重用直接缓冲区或 执行其他聪明的技巧来提升 吞吐量。如果你只是在创造 一次性使用的缓冲区 差异不显着。在 另一方面,如果你将使用 缓冲在一个 高性能场景,你 最好分配直接缓冲区 并重复使用它们。
直接缓冲区是I / O的最佳选择, 但他们可能会更贵 创建比非直接字节缓冲区。 直接缓冲区使用的内存是 通过呼叫分配 原生的,特定于操作系统的 代码,绕过标准JVM堆。 直接设置和拆除 缓冲区可能会更多 比堆驻留缓冲区贵, 取决于主机操作系统 和JVM实现。该 直接缓冲区的内存存储区域 不受垃圾收集 因为它们超出了标准 JVM堆。
使用的性能权衡 直接缓冲区与非直接缓冲区可以 JVM,操作系统, 和代码设计。通过分配内存 在堆外,你可能会受到你的影响 适用于额外的力量 JVM没有意识到这一点。什么时候 带来额外的运动部件 玩,确保你实现了 期望的效果。我推荐 旧软件格言:首先要做到 工作,然后快速。别担心 预先优化太多; 首先关注正确性。该 JVM实现也许能够 执行缓冲区缓存或其他 优化将给你的 你需要的性能没有太多 你不必要的努力。
答案 1 :(得分:25)
没有理由期望直接缓冲区更快地访问内部 jvm。当您将它们传递给本机代码时,它们的优势就来了 - 例如,各种渠道背后的代码。
答案 2 :(得分:18)
因为DirectByteBuffers是直接的 操作系统级别的内存映射
他们不是。它们只是普通的应用程序进程内存,但在Java GC期间不受重定位,这大大简化了JNI层内部的事物。您所描述的内容适用于MappedByteBuffer
。
它可以更快地执行get / put调用
结论不是从前提中得出的;前提是假的;结论也是错误的。一旦进入JNI层,它们就会更快,如果你从同一个DirectByteBuffer
读取和写入它们更多更快,因为数据根本不必跨越JNI边界
答案 3 :(得分:17)
最好自己进行测量。快速回答似乎是从allocateDirect()
缓冲区发送的时间比allocate()
变体(测试为将文件复制到/ dev / null)少25%到75%,具体取决于大小,但是分配本身可以显着更慢(即使是100倍)。
来源: