在 java 中释放直接缓冲区和可能的陷阱

Releasing a direct buffer in java and possible pitfalls

我们有如下一段代码:

long buffer = ((DirectBuffer) ByteBuffer.allocateDirect(256)).address(); 

似乎线程堆栈上没有对直接缓冲区(作为对象)的引用。所以,这意味着该对象是 幻影可达.

  • DirectByteBuffer becomes phantom-reachable.
  • Garbage collection is performed (in separate thread), DirectByteBuffer Java object is collected and an entry is added to the ReferenceQueue.
  • Cleaner thread reaches this entry and runs the registered clean-up action (in this case, it's java.nio.DirectByteBuffer.Deallocator object), this action finally frees the native memory.

引文来自:

因此,有可能分配的内存可以释放。但是,我们有一个指向它的指针,类型为 longbuffer。因此,我们有可能得到 SIGSEGV 或类似的东西。

我的问题是:

这是否意味着我们会以这种方式使用 DirectBuffer 伤害自己?

严格来说,并不是按照您描述的那样,因为您只是将 long 值存储到 long 变量中。使用此值,您可以在 Java.

中做任何有害的事情

一旦您将其传递给某些本机 (C/C++) 代码并开始将其用作向其写入内容的地址,您就会陷入问题,因为对象的内存可能已经被回收由垃圾收集器。

因此,所有这些访问都应该在本机代码 (JNI) 中完成,您可以在本机代码中使用本机 API 告诉虚拟机您的本机代码持有一个引用(或不再持有)。然后,您可以在本机代码中使用 address() 函数来获取地址并使用它。

您对这里的危险假设是正确的,但需要注意。

在包含的 ByteBuffer 对象符合垃圾回收条件后,可以随时释放直接缓冲区的后备缓冲区(释放本机内存的确切时间取决于实现,但通常大约发生在字节缓冲区运行)。

从标准 Java 来看,这个 "dangling pointer" 不会造成真正的问题,因为它和另一个一样长,你不能不安全地使用。当然,如果您将它传递给某些本机代码或不安全代码并尝试将其用作指针,事情可能会崩溃。