我们需要手动释放已分配的ByteBuffer吗?

5
我正在编写一些使用 ByteBuffers 的代码。在API文档中,有这样的说明:

没有办法显式地释放缓冲区(除非使用特定于JVM的反射)。缓冲区对象受到GC的限制,通常需要两个GC周期才能释放缓冲区对象变得不可访问后的堆外存储器。

然而,在一个SO帖子的被采纳的答案中,我读到了以下内容:

BigMemory使用JVM进程的内存地址空间,通过直接使用字节缓冲区,与其他本机JAVA对象不同,不受GC的限制

现在应该怎么办?我应该释放已创建的缓冲区吗?还是我误解了文档或回答中的某些内容?


好的。谷歌先弹出了文档,然后是一些SO帖子,这导致了误解,因此提出了问题。把链接放在这里对我和其他可能遇到这篇文章的人都很有帮助。 - agiro
2个回答

3
这取决于你如何创建缓冲区,有许多可能的用例。通常情况下,ByteBuffer.allocate()将在堆上创建并被GC回收。其他选项(例如本地内存)则可能不会被回收。
Terracotta BigMemory是一种本地非堆内存,不受JVM GC控制。如果您在此类内存中分配缓冲区,则必须自行清除它。
即使在堆内存中分配缓冲区,清除缓冲区也可能是一个好主意。GC会负责收集未使用的缓冲区,但这需要一些时间。

谢谢,但混淆的原因是“通过直接字节缓冲区而不受GC控制”的部分。那么Terracotta BM是否以不同的方式使用ByteBuffer?还是它们只是同名巧合? - agiro

3
根据LWJGL中的BufferUtils文档,没有明确的方法可以显式地释放ByteBuffer。使用标准机制(即通过直接或间接调用ByteBuffer#allocateDirect)分配的ByteBuffer对象会受到垃圾回收的影响,并最终被清理。
您链接的答案似乎特别涉及BigMemory库。使用JNI,您可以创建一个(直接的)ByteBffer,它不受垃圾回收处理,您需要实际释放底层数据。
然而,一个简短的建议:在处理依赖于(直接的)ByteBuffer对象进行数据传输到本地端的LWJGL和其他库时,应考虑这些缓冲区的使用模式。特别是对于OpenGL绑定库,您通常需要一个仅具有16个float值空间的ByteBuffer,例如(例如包含发送到OpenGL的矩阵)。在许多情况下,使用这些缓冲区进行数据传输的方法将频繁调用。
在这种情况下,通常不建议重复分配这些小型、短寿命的缓冲区:
class Renderer {
    void renderMethodThatIsCalledThousandsOfTimesPerSecond() {
        ByteBuffer bb = ByteBuffer.allocateDirect(16 * 4);
        fill(bb);
        passToOpenGL(bb);
    }
}

这些缓冲区的创建和GC会显著降低性能,而且令人烦恼的是,GC暂停可能导致游戏卡顿。在这种情况下,最好将分配拆分出来并重新使用缓冲区。"最初的回答"
class Renderer {

    private final ByteBuffer MATRIX_BUFFER_4x4 = ByteBuffer.allocateDirect(16 * 4);

    void renderMethodThatIsCalledThousandsOfTimesPerSecond() {
        fill(MATRIX_BUFFER_4x4);
        passToOpenGL(MATRIX_BUFFER_4x4);
    }
}

感谢您的回答,出于性能原因,这个问题值得一提。 目前我只是在Java代码中使用Assimp,但害怕会有内存泄漏。 - agiro

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