是啥阻止了我的 ByteBuffer 释放?

Posted

技术标签:

【中文标题】是啥阻止了我的 ByteBuffer 释放?【英文标题】:What's holding up my ByteBuffer from freeing?是什么阻止了我的 ByteBuffer 释放? 【发布时间】:2011-10-31 03:33:14 【问题描述】:

我分配了很多字节缓冲区。在我完成它们之后,我将所有引用设置为 null。这应该是释放字节缓冲区的“正确”方式?取消引用它并让 GC 清理它?我还调用 System.gc() 来尝试帮助它。

无论如何,我创建了一堆缓冲区,尊重它们;但在“一段时间”之后,我得到了各种各样的内存错误:java.lang.OutOfMemoryError: Direct buffer memory

我可以增加 MaxDirectMemorySize 但它只是延迟了上述错误。

我 99% 肯定我没有任何引用旧 ByteBuffers 的东西。有没有办法检查这个,看看到底还有什么分配的 ByteBuffer?

【问题讨论】:

对假定未引用的数据结构的引用通常隐藏在集合类中。其他地方包括侦听器和其他内部类构造。我会先检查那些地方。 【参考方案1】:

您可以使用 Eclipse 中免费的 MAT 等工具,通过让其进行堆转储分析来查看是什么保留了您的字节缓冲区。

我能想到的另一种方法是用其他具有终结器方法的东西包装你的字节缓冲区。

此外 Systen.gc() 不保证终结器将被执行,您需要执行 System.runFinalization() 以增加可能性。

【讨论】:

【参考方案2】:

将引用设置为 null 是让垃圾收集器完成该对象的正确方法。一定还有其他一些悬空的参考。我发现查找内存泄漏的最佳工具是YourKit。一个非常好的免费替代方案是Visual VM from the JDK。

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

【讨论】:

【参考方案3】:

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

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

但是,自己回收直接缓冲区是一种更好的方法,因此这样做并不那么重要。 (直接创建ByteBuffers比较昂贵)

【讨论】:

【参考方案4】:

Direct java.nio.ByteBuffer(根据定义)存储在 java 堆空间之外。在 GC 在堆上运行并释放它之前,它不会被释放。但是你可以想象这样一种情况,堆使用率很低,因此它不需要 GC,但非堆内存被分配并超出界限。

基于非常有趣的阅读: http://www.ibm.com/developerworks/library/j-nativememory-linux/

病态的情况是本机堆变满并且 一个或多个直接 ByteBuffers 符合 GC 条件(并且可以被释放 在本机堆上腾出一些空间),但 Java 堆主要是 为空,因此不会发生 GC。

【讨论】:

以上是关于是啥阻止了我的 ByteBuffer 释放?的主要内容,如果未能解决你的问题,请参考以下文章

mySQL:是啥阻止了我的外键约束?

使用 Swift 4 的 Codable 进行解码时,是啥阻止了我从 String 到 Int 的转换?

如何重新使用 ByteBuffer 将几个 512 字节的块写入套接字?

这个 Java ByteBuffer 的行为有解释吗?

ByteBuffer 的初始“模式”是啥?

在 JNA 调用中使用 ByteBuffer 表示字符串会导致缓冲区中出现额外字符