ByteBuffer.allocate()与ByteBuffer.allocateDirect()

时间:2011-04-14 23:28:39

标签: java performance nio bytebuffer

allocate()allocateDirect(),这就是问题。

多年以来,我一直坚持认为,因为DirectByteBuffer是OS级别的直接内存映射,所以get / put调用的执行速度比HeapByteBuffer更快。到目前为止,我从未真正有兴趣了解有关情况的具体细节。我想知道这两种ByteBuffer中哪一种更快,哪种条件更快。

4 个答案:

答案 0 :(得分:141)

罗恩希奇在他出色的书Java NIO中似乎提供了我认为可以很好地回答你的问题:

  

操作系统执行I / O.   对记忆区域的操作。这些   记忆区域,就操作而言   系统而言,是连续的   字节序列。这并不奇怪   然后只有字节缓冲区   有资格参加I / O.   操作。还记得那个   操作系统将直接访问   进程的地址空间   这种情况是JVM进程,要转移   数据。这意味着内存区域   这是I / O的目标必须   是连续的字节序列。在   JVM,字节数组可能不是   连续存储在内存中,或者   垃圾收集器可以在任何地方移动它   时间。数组是Java中的对象,和   数据存储在其中的方式   对象可能因JVM而异   实施到另一个。

     

出于这个原因,a的概念   引入了直接缓冲区。直接   缓冲区用于交互   使用通道和本机I / O例程。   他们尽最大努力存储   a。内存区域中的字节元素   通道可用于直接或原始,   通过使用本机代码来进行访问   操作系统要排空或填充   直接记忆区域。

     

直接字节缓冲区通常是   I / O操作的最佳选择。通过   设计,他们支持最多   高效的I / O机制   JVM。非直接字节缓冲区可以是   传递到频道,但这样做可能   招致绩效惩罚。它的   通常不可能是非直接的   缓冲区是本机的目标   I / O操作。如果您传递非直接   ByteBuffer对象为一个通道   写,通道可能含蓄地做   每次通话都有以下内容:

     
      
  1. 创建临时直接ByteBuffer   对象。
  2.   
  3. 复制非直接内容   缓冲区到临时缓冲区。
  4.   
  5. 执行低级I / O操作   使用临时缓冲区。
  6.   
  7. 临时缓冲区对象熄灭   范围,最终是垃圾   收集。
  8.         

    这可能会导致缓冲   在每个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倍)。

来源: