如何垃圾收集直接缓冲java

mgl*_*mnc 28 java buffer memory-leaks bytebuffer

我有一个内存泄漏,我已经隔离到不正确的直接字节缓冲区.

ByteBuffer buff = ByteBuffer.allocateDirect(7777777);

GC收集包含这些缓冲区的对象,但不会丢弃缓冲区本身.如果我实例化足够的包含缓冲区的瞬态对象,我会得到这个令人鼓舞的消息.

java.lang.OutOfMemoryError: Direct buffer memory

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

buff.clear();

System.gc();

不起作用

Ste*_*n C 16

我怀疑你的应用程序的某个地方有对ByteBuffer实例的引用,并且阻止它被垃圾收集.

直接ByteBuffer的缓冲区内存在普通堆之外分​​配(这样GC就不会移动它!!).但是,ByteBuffer API没有提供显式处理/解除分配缓冲区的方法.所以我假设垃圾收集器会这样做......一旦它确定不再引用ByteBuffer对象.


Li *_* Pi 16

一旦DBB到达引用队列,它就会被释放,并运行终结器.但是,由于我们不能依赖终结器来运行,我们可以使用反射来手动调用它的"清洁器".

使用反射:

/**
* 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);

}
Run Code Online (Sandbox Code Playgroud)

  • 值得记录的是,这种方法可能会在JRE版本之间中断(尽管这种方式不太可能).Oracle不保证内部构造的API向后兼容性. (4认同)
  • 可以将ByteBuffer强制转换为DirectBuffer,然后调用.cleaner().clean() (2认同)
  • Luke 是对的,你的代码与 Java 1.9 不同,我的:http://stackoverflow.com/a/26777380/458157 (2认同)

Gre*_*ill 12

ByteBuffer文件说:

可以通过调用allocateDirect此类的工厂方法来创建直接字节缓冲区.与非直接缓冲区相比,此方法返回的缓冲区通常具有更高的分配和解除分配成本.直接缓冲区的内容可能位于正常的垃圾收集堆之外,因此它们对应用程序的内存占用量的影响可能并不明显.因此,建议直接缓冲区主要分配给受基础系统本机I/O操作影响的大型长期缓冲区.通常,最好只在它们在程序性能上产生可测量的增益时才分配直接缓冲区.

特别是,语句"可能位于正常的垃圾收集堆之外"似乎与您的示例相关.


And*_*s_D 5

分配的内存通过本机库实现.当调用ByteBuffer#finalize方法时,将释放该内存,当缓冲区为gc'd时,该内存将被释放.看看DirectByteBufferImpl的allocate()和finalize()实现.

buff.clear()没有必要,System.gc()只有像其他已经提到的那样,没有更多的参考留给ByteBuffer对象,这只会有所帮助.

  • 我的猜测是System.gc在任何情况下都无济于事.我希望没有足够缓冲存储器的"事件"会触发GC以试图释放旧缓冲区.只有在GC之后没有足够的缓冲存储器时才会抛出OOM异常. (2认同)