对于allocate()或allocateDirect(),这就是问题所在。
多年来,我一直认为,由于DirectByteBuffers是操作系统级别的直接内存映射,因此使用get/put调用会比HeapByteBuffers执行得更快。直到现在,我才真正有兴趣找出有关这种情况的确切细节。我想知道两种类型的ByteBuffer中哪一种更快,以及在什么条件下。
发布于 2011-04-15 10:48:22
Ron Hitches在他的优秀著作Java NIO中似乎提供了我认为可以很好地回答你的问题的答案:
操作系统在内存区域上执行I/O操作。就操作系统而言,这些存储区是连续的字节序列。因此,只有字节缓冲区有资格参与I/O操作也就不足为奇了。还要回想一下,操作系统将直接访问进程的地址空间,在本例中是JVM进程,以传输数据。这意味着作为I/O操作目标的内存区必须是连续的字节序列。在JVM中,字节数组可能不会连续存储在内存中,或者垃圾收集器可以随时移动它。数组是Java中的对象,在该对象中存储数据的方式可能因JVM实现的不同而不同。
出于这个原因,引入了直接缓冲区的概念。直接缓冲区用于与通道和本机I/O例程进行交互。它们尽最大努力将字节元素存储在通道可用于直接或原始访问的内存区中,方法是使用本机代码告诉操作系统直接排出或填充内存区。
直接字节缓冲区通常是I/O操作的最佳选择。根据设计,它们支持JVM可用的最有效的I/O机制。可以将非直接字节缓冲区传递给通道,但这样做可能会导致性能损失。非直接缓冲区通常不可能成为本机I/O操作的目标。如果将非直接ByteBuffer对象传递给通道以进行写入,则通道可能会在每次调用时隐式执行以下操作:
这可能会在每次I/O时导致缓冲区复制和对象混乱,这正是我们希望避免的事情。然而,根据实现的不同,情况可能不会那么糟糕。运行时可能会缓存和重用直接缓冲区,或者执行其他聪明的技巧来提高吞吐量。如果您只是创建一个一次性使用的缓冲区,那么差异并不显著。另一方面,如果您将在高性能场景中重复使用缓冲区,则最好分配直接缓冲区并重用它们。
直接缓冲区是I/O的最佳选择,但它们的创建成本可能比非直接字节缓冲区更高。直接缓冲区使用的内存是通过调用本机特定于操作系统的代码来分配的,绕过了标准的JVM堆。根据主机操作系统和JVM实现的不同,设置和拆除直接缓冲区的成本可能比驻留在堆中的缓冲区高得多。直接缓冲区的内存存储区域不受垃圾收集的影响,因为它们在标准JVM堆之外。
使用直接缓冲区和非直接缓冲区的性能权衡可能会因JVM、操作系统和代码设计的不同而大不相同。通过在堆之外分配内存,您的应用程序可能会受到JVM没有意识到的额外压力的影响。当使用额外的移动部件时,确保你达到了想要的效果。我推荐旧的软件格言:首先让它工作,然后让它变得更快。不要过于担心前面的优化;首先要专注于正确性。JVM实现可能能够执行缓冲区缓存或其他优化,这将为您提供所需的性能,而无需您进行大量不必要的工作。
发布于 2011-04-15 09:20:25
没有理由期望直接缓冲区在jvm中的访问速度更快。当您将它们传递给本机代码时,它们的优势就会显现出来--例如,所有类型的通道背后的代码。
发布于 2011-04-17 09:56:33
,因为DirectByteBuffers是操作系统级别的直接内存映射
它们不是。它们只是普通的应用程序进程内存,但在Java GC期间不会受到重新定位的影响,这大大简化了JNI层内的事情。您所描述的内容适用于MappedByteBuffer。
表示,get/put调用的执行速度会更快
结论不是从前置得到的;前置是错误的;结论也是错误的。一旦您进入JNI层,它们就会更快,如果您从同一个DirectByteBuffer读取和写入数据,它们会更快,因为数据根本不需要跨越JNI边界。
https://stackoverflow.com/questions/5670862
复制相似问题