如何在 Java 中对直接缓冲区进行垃圾回收

2022-09-01 09:38:22

我有一个内存泄漏,我已经隔离到错误地释放了直接字节缓冲区。

ByteBuffer buff = ByteBuffer.allocateDirect(7777777);

GC 收集包含这些缓冲区的对象,但不释放缓冲区本身。如果我实例化了足够多的包含缓冲区的瞬态对象,我会收到以下令人鼓舞的消息:

java.lang.OutOfMemoryError: Direct buffer memory

我一直在寻找这个问题,显然

buff.clear();

System.gc();

不起作用。


答案 1

我怀疑您的应用程序在某个地方引用了ByteBuffer实例,这可以防止它被垃圾回收。

直接字节缓冲器的缓冲区内存分配在正常堆之外(以便 GC 不会移动它!!)。但是,ByteBuffer API 不提供显式释放/解除分配缓冲区的方法。所以我假设垃圾收集器会这样做...一旦它确定不再引用 ByteBuffer 对象。


答案 2

DBB 在到达引用队列并运行终结器后将被解除分配。但是,由于我们不能依赖终结器来运行,因此我们可以使用反射手动调用其“cleaner”。

使用反射:

/**
* DirectByteBuffers are garbage collected by using a phantom reference and a
* reference queue. Every once a while, the JVM checks the reference queue and
* cleans the DirectByteBuffers. However, as this doesn't happen
* immediately after discarding all references to a DirectByteBuffer, it's
* easy to OutOfMemoryError yourself using DirectByteBuffers. This function
* explicitly calls the Cleaner method of a DirectByteBuffer.
* 
* @param toBeDestroyed
*          The DirectByteBuffer that will be "cleaned". Utilizes reflection.
*          
*/
public static void destroyDirectByteBuffer(ByteBuffer toBeDestroyed)
    throws IllegalArgumentException, IllegalAccessException,
    InvocationTargetException, SecurityException, NoSuchMethodException {

  Preconditions.checkArgument(toBeDestroyed.isDirect(),
      "toBeDestroyed isn't direct!");

  Method cleanerMethod = toBeDestroyed.getClass().getMethod("cleaner");
  cleanerMethod.setAccessible(true);
  Object cleaner = cleanerMethod.invoke(toBeDestroyed);
  Method cleanMethod = cleaner.getClass().getMethod("clean");
  cleanMethod.setAccessible(true);
  cleanMethod.invoke(cleaner);

}

推荐