我的ByteBuffer为什么无法释放?

3
我正在分配大量的字节缓冲区。在我完成它们后,我将所有引用设置为null。这是释放bytebuffer的“正确”方法吗?取消引用并让GC清理它?我还调用System.gc()来尝试帮助它。
无论如何,我创建了一堆缓冲区,取消引用它们;但是在“一段时间”之后,我会得到各种内存错误:java.lang.OutOfMemoryError:直接缓冲区内存
我可以增加MaxDirectMemorySize,但它只会延迟上述错误。
我99%确定没有任何东西引用旧的ByteBuffers。有没有办法检查这个并查看仍然分配了ByteBuffer的是什么?

对于那些看似未被引用的数据结构,它们往往隐藏在集合类中。其他可能的地方包括监听器和其他内部类结构。我建议首先检查这些地方。 - Ted Hopp
4个回答

1

你可以使用像Eclipse附带的免费工具MAT来查看是什么导致了你的字节缓冲区保持不变,通过进行一些堆转储分析。

我还能想到另一种方法,就是用一个带有finalizer方法的其他对象包装你的字节缓冲区。

此外,Systen.gc()不能保证最终器会被执行,你需要执行System.runFinalization()来增加可能性。


1

将引用设置为 null 是让垃圾回收器知道你已经完成该对象处理的正确方法。可能仍有其他悬空引用存在。我发现寻找内存泄漏最好的工具是 YourKit。还有一个非常好的免费替代品是 JDK 中的 Visual VM

请记住,slice() 操作会创建一个新的字节缓冲区,它引用了第一个缓冲区。


0

这是一个与旧版本Java有关的问题。最新版本的Java 6在抛出OutOfMemoryError之前将调用System.gc()。如果您不想触发GC,可以在Oracle/Sun JVM上手动释放直接内存。

((DirectBuffer) buffer).cleaner().clean();

然而,更好的方法是自己回收直接缓冲区,因此这样做并不那么关键。(创建直接ByteBuffer相对昂贵)


0
直接的`java.nio.ByteBuffer`(根据定义)存储在Java堆空间之外。它只有在堆上运行GC并释放它时才会被释放。但是你可以想象一种情况,堆使用率很低因此不需要GC,但非堆内存被分配并超出范围。
基于非常有趣的阅读: http://www.ibm.com/developerworks/library/j-nativememory-linux/ 引用:
"病态情况是,本机堆栈变满,一个或多个直接ByteBuffer符合GC条件(可以被释放以在本机堆栈上释放一些空间),但Java堆栈几乎为空,所以没有进行GC。"

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接