在ByteBuffer
的java文档中提到,由allocateDirect
分配的直接缓冲区具有更高的分配和释放成本。
直接字节缓冲区可以通过调用该类的allocateDirect工厂方法来创建。此方法返回的缓冲区通常比非直接缓冲区具有稍高的分配和释放成本。
我想知道为什么会有更高的成本?在引擎盖下需要做什么?
由于mmap
的成本,我可以理解MappedByteBuffer
的成本较高。但是在我看来,DirectByteBuffer
只是JVM进程中的一个内存块,就像Java堆一样。它们都在JVM进程堆中。
我不知道在java堆内部或外部分配或释放内存块有什么区别。
2条答案
按热度按时间rxztt3cl1#
取决于很多因素,它更有可能导致显式的
malloc
调用,而不仅仅是.allocate
,尽管它取决于OS+Arch,并且它很可能是.allocate
的别名。一些背景和细节,以防答案不是特别令人满意:
参见this answer了解一些上下文。
通常,java中的对象(特别是字节数组,它是基本HeapByteBuffer的底层数据存储,由
.allocate
创建)具有以下属性:使用
.allocateDirect
,您可以打破一系列规则:-Xmx
之外的参数,作为示例,该参数用于设置最大堆大小)。malloc
调用,向操作系统请求一个连续的ram块。malloc
调用可能需要时间。malloc
创建了它。请注意,这里的理论存在一些漏洞-如果它显式地为
malloc
艾德,那么它应该显式地为free
d,但ByteBuffer既没有deallocate()
方法,也不是(Auto)Closable。另外,javadoc保留了很多权利;例如,.allocateDirect
可能与allocate
没有什么不同-JVM可以自由地使用堆 * 或不使用 *。这些直接的操作系统级交互往往会这样做:Java必须在各种OS+Architecture组合上运行。如果一些OS+arch组合没有直接缓冲区,现在怎么办?.allocateDirect
是否应该快速失败(抛出异常)?这将是明智的,除非,只有当规范明确锁定了直接字节缓冲区可以保证什么,这就存在一个问题,因为在OS+Arch组合中有大量的变化。“不移动”是各种低级I/O OS内核调用的要求(在这种情况下,您告诉内核:请直接告诉系统中的网络硬件将传入的字节直接复制到这个内存块中-这种低级I/O。并非所有操作系统都支持它,也并非所有操作系统都以相同的方式支持它)。一个普通的堆缓冲区不能使用它;如果底层操作系统支持它,JVM也支持它,JVM必须建立自己的直接缓冲区(在堆之外/在与GC隔离的区域中,以确保它不会移动),并启动一个单独的进程将这些字节blit到您的缓冲区中,考虑到GC系统可以四处移动。相反,如果你问例如:一个
FileChannel
对象将字节从文件复制到你的直接缓冲区,它可能是可能的,你的JVM的原生impl支持FileChannel
只会告诉操作系统告诉SSD * 直接 * 这样做,没有来自操作系统/CPU的任何交互。有些硬件可以做到这一点。但仅限于“固定”存储器位置。你的JVM是否真的能做到这一切--没有保证。但是如果可以的话,它只能在你做一个直接缓冲区的时候才能做到。
wfveoks02#
分配一个非直接的
ByteBuffer
可以被垃圾收集器完全优化,因此它可能比分配一个直接的ByteBuffer
更有效。在实践中,差异可能并不那么显著。更大的问题是去配置。这两种类型的
ByteBuffers
只能通过垃圾回收来释放,但直接ByteBuffers
通常需要更昂贵的旧代回收。因此,不建议使用短期直接ByteBuffers
,因为它会增加GC负载。请注意,有一种不受支持的方法可以显式删除直接
ByteBuffers
,但是在java.lang.foreign API完全发布之后,这种功能将在某个时候消失。如果你需要短期的堆外缓冲区,那么应该使用MemorySegments
。