如何在 Java 中对直接缓冲区进行垃圾回收
我有一个内存泄漏,我已将其隔离到错误处理的直接字节缓冲区。
ByteBuffer buff = ByteBuffer.allocateDirect(7777777);
GC 收集包含这些缓冲区的对象,但不会处置缓冲区本身。如果我实例化足够多的包含缓冲区的瞬态对象,我会得到这个令人鼓舞的消息:
java.lang.OutOfMemoryError: Direct buffer memory
我一直在寻找这个问题,显然
buff.clear();
并
System.gc();
不起作用。
I have a memory leak that I have isolated to incorrectly disposed direct byte buffers.
ByteBuffer buff = ByteBuffer.allocateDirect(7777777);
The GC collects the objects that harbor these buffers but does not dispose of the buffer itself. If I instantiate enough of the transient objects containing buffers, I get this encouraging message:
java.lang.OutOfMemoryError: Direct buffer memory
I have been searching up this problem and apparently
buff.clear();
and
System.gc();
do not work.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(7)
我怀疑您的应用程序在某个地方引用了 ByteBuffer 实例,这导致它无法被垃圾收集。
直接 ByteBuffer 的缓冲内存是在普通堆之外分配的(这样 GC 就不会移动它!!)。但是,ByteBuffer API 没有提供显式处置/取消分配缓冲区的方法。所以我假设垃圾收集器会这样做......一旦它确定 ByteBuffer 对象不再被引用。
I suspect that somewhere your application has a reference to the ByteBuffer instance(s) and that is preventing it from being garbage collected.
The buffer memory for a direct ByteBuffer is allocated outside of the normal heap (so that the GC doesn't move it!!). However, the ByteBuffer API provides no method for explicitly disposing of / deallocating a buffer. So I assume that the garbage collector will do it ... once it determines that the ByteBuffer object is no longer referenced.
DBB 一旦到达引用队列就会被释放,并且终结器将运行。但是,由于我们不能依赖终结器来运行,因此我们可以使用反射来手动调用其“清理器”。
使用反射:
The DBB will be deallocated once it hits the reference queue, and the finalizer is run. However, as we cannot depend on a finalizer to run, we can use reflection to manually call its "cleaner".
Using reflection:
ByteBuffer
文档说:特别是,语句“可能驻留在正常的垃圾收集堆之外”似乎与您的示例相关。
The
ByteBuffer
documentation says:In particular, the statement "may reside outside of the normal garbage-collected heap" seems relevant to your example.
分配的内存是通过本机库实现的。当调用 ByteBuffer#finalize 方法时,即当 Buffer 被 gc'd 时,该内存将被释放。查看 DirectByteBufferImpl 的 allocate() 和 Finalize() 实现。
buff.clear()
不是必需的,System.gc()
只有在像其他人已经提到的那样,不再有 ByteBuffer 对象的引用时才会有帮助。The allocated memory is realized through a native libary. This memory will be freed when the ByteBuffer#finalize method is called, iaw when the Buffer is gc'd. Have a look at the allocate() and finalize() implementations of DirectByteBufferImpl.
buff.clear()
is not necessary,System.gc()
will only help if, like others already mentioned, there's no more reference left to the ByteBuffer object.这是一个适用于任何直接缓冲区的改进实现:
Here is a refined implementation that will work for any direct buffer:
只要您依赖于 sun (oracle) 特定的实现,比尝试更改 java.nio.DirectByteBuffer 的可见性更好的选择是通过反射使用 sun.nio.ch.DirectBuffer 接口。
As long as you are relying on sun (oracle) specific implementation, a better choice than trying to change the visibility of java.nio.DirectByteBuffer is to use the sun.nio.ch.DirectBuffer interface via reflections.
这个问题的现有答案中缺少许多警告,例如在 JDK 9+ 下运行时要求模块描述符包含
requires jdk.unsupported
,无法访问MappedByteBuffer.cleaner( )
由于强封装的强制执行,在 JDK 16+ 下没有解决方法,以及在 JDK 7-16 下使用SecurityManager
运行的要求等。我在这里查看完整的详细信息:< a href="https://stackoverflow.com/a/54046774/3950982">https://stackoverflow.com/a/54046774/3950982
There are many caveats missing from the existing answers to this question, e.g. requirements when running under JDK 9+ for the module descriptor to contain
requires jdk.unsupported
, the inability to accessMappedByteBuffer.cleaner()
without workarounds under JDK 16+ due to the enforcement of strong encapsulation, the requirements if running with aSecurityManager
under JDK 7-16, etc. I go over the full details here:https://stackoverflow.com/a/54046774/3950982