在Java中为JOGL释放直接缓冲区本机内存

在Java中为JOGL释放直接缓冲区本机内存,java,memory-management,nio,jogl,buffer,Java,Memory Management,Nio,Jogl,Buffer,我使用直接缓冲区(java.nio)来存储JOGL的顶点信息。这些缓冲区很大,在应用程序生命周期中会被多次更换。内存没有及时释放,在进行了几次替换后,内存已用完 似乎没有很好的方法使用java.nio的缓冲区类来解除分配。我的问题是: JOGL中是否有删除直接缓冲区的方法?我正在查看glDeleteBuffer(),但这似乎只是从显卡内存中删除缓冲区 谢谢取消分配直接缓冲区是在标记ByteBuffer对象后的一段时间内由垃圾收集器完成的一项工作 您可以在删除对缓冲区的最后一个引用后立即尝试调用g

我使用直接缓冲区(java.nio)来存储JOGL的顶点信息。这些缓冲区很大,在应用程序生命周期中会被多次更换。内存没有及时释放,在进行了几次替换后,内存已用完

似乎没有很好的方法使用java.nio的缓冲区类来解除分配。我的问题是:

JOGL中是否有删除直接缓冲区的方法?我正在查看glDeleteBuffer(),但这似乎只是从显卡内存中删除缓冲区


谢谢

取消分配直接缓冲区是在标记ByteBuffer对象后的一段时间内由垃圾收集器完成的一项工作


您可以在删除对缓冲区的最后一个引用后立即尝试调用gc。至少内存的释放速度有可能会快一点。

直接缓冲区很棘手,没有通常的垃圾收集保证-有关更多详细信息,请参阅:


如果您遇到问题,我建议只分配一次并重新使用缓冲区,而不是重复分配和取消分配。

取消分配的方式很糟糕——软引用基本上插入到一个更干净的对象中,当拥有的ByteBuffer被垃圾收集时,它会取消分配。但这并不能保证能够及时调用。

直接NIO缓冲区使用非托管内存。这意味着它们是在本机堆上分配的,而不是在Java堆上分配的。因此,只有当JVM在Java堆上而不是在本机堆上耗尽内存时,才会释放它们。换句话说,它是非托管的=由您来管理它们。不鼓励强制垃圾收集,并且在大多数情况下都无法解决此问题

当您知道直接NIO缓冲区对您来说已经无用时,您必须使用它的sun.misc.Cleaner(StaxMan是对的)释放它的本机内存,并调用clean()(Apache Harmony除外)、call free()(Apache Harmony)或使用更好的公共API来实现这一点(可能在Java>12中,自动清理扩展了AutoCloseable?)

这不是JOGL的工作,您可以使用普通Java代码自己完成。在GPL v2下,并且在更为许可的许可证下

编辑:与Java 1.9兼容,支持OpenJDK、Oracle Java、Sun Java、Apache Harmony、GNU类路径和Android。您可能需要删除一些语法上的糖分,以使Java<1.7(多捕获、菱形和泛型)正常工作

参考:

Direct ByteBuffer对象自动清理其本机缓冲区 但是,它们只能作为Java堆GC的一部分来执行,所以它们不能 自动响应本机堆上的压力。GC只会发生 当Java堆变得如此满时,它无法为堆分配服务 请求,或者如果Java应用程序显式请求它(不是 建议使用,因为它会导致性能问题)

参考:

直接缓冲区的内容可能位于正常垃圾收集堆之外

集成在Java 14中:

try (MemorySegment segment = MemorySegment.allocateNative(100)) {
   ...
}
在Java 16中,您可以通过调用将字节缓冲区包装到内存段中,获取其内存地址并释放它(这是一种受限方法):

CLinker.getInstance().freeMemoryRestricted(MemorySegment.ofByteBuffer(myByteBuffer).address());
请注意,您仍然需要在许多非常重要的情况下使用反射,以便找到可以释放的缓冲区,通常是当您的直接NIO缓冲区不是ByteBuffer时

注意:sun.misc.Cleaner已经在模块“Java.base”中的Java 1.9中移动到了,后者实现了Java.lang.Runnable(感谢Alan Bateman提醒我这一差异),时间很短,但是没有。您必须调用sun.misc.Unsafe.invokeCleaner(),这是错误的。我更喜欢使用清洁剂作为一种可运行的清洁剂,因为它避免了依赖sun.misc.Unsafe,但它现在不起作用

我的最后一个建议适用于Java9、10、11和

需要使用孵化特性(需要Java>=14),但非常简单


在一个更为宽松的许可证下是有限制的。

与滥用非公共API的反射相比,您可以完全在受支持的公共API中轻松地执行此操作

编写一些JNI,用NewDirectByteBuffer(记住设置顺序)包装malloc,并编写一个类似的函数来释放它。

使用中的信息,我能够将这个实用程序类放在一起,以释放给定ByteBuffer的直接内存分配。当然,这只能作为最后的手段,不应该真正用于生产代码中

在JavaSE版本10.0.2中测试和工作

public final class BufferUtil {

    //various buffer creation utility functions etc. etc.

    protected static final sun.misc.Unsafe unsafe = AccessController.doPrivileged(new PrivilegedAction<sun.misc.Unsafe>() {
        @Override
        public Unsafe run() {
            try {
                Field f = Unsafe.class.getDeclaredField("theUnsafe");
                f.setAccessible(true);
                return (Unsafe) f.get(null);
            } catch(NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException ex) {
                throw new RuntimeException(ex);
            }
        }
    });

    /** Frees the specified buffer's direct memory allocation.<br>
     * The buffer should not be used after calling this method; you should
     * instead allow it to be garbage-collected by removing all references of it
     * from your program.
     * 
     * @param directBuffer The direct buffer whose memory allocation will be
     *            freed
     * @return Whether or not the memory allocation was freed */
    public static final boolean freeDirectBufferMemory(ByteBuffer directBuffer) {
        if(!directBuffer.isDirect()) {
            return false;
        }
        try {
            unsafe.invokeCleaner(directBuffer);
            return true;
        } catch(IllegalArgumentException ex) {
            ex.printStackTrace();
            return false;
        }
    }

}
公共最终类BufferUtil{
//各种缓冲区创建实用程序功能等。
受保护的静态最终sun.misc.Unsafe不安全=AccessController.doPrivileged(新权限(){
@凌驾
公共不安全运行(){
试一试{
字段f=不安全的.class.getDeclaredField(“不安全的”);
f、 setAccessible(true);
返回(不安全)f.get(空);
}捕获(NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException ex){
抛出新的运行时异常(ex);
}
}
});
/**释放指定缓冲区的直接内存分配。
*调用此方法后不应使用缓冲区;应 *而是允许通过删除它的所有引用来对其进行垃圾收集 *从你的节目中。 * *@param directBuffer将分配内存的直接缓冲区 *释放 *@return是否释放了内存分配*/ 公共静态最终布尔freeDirectBufferMemory(ByteBuffer directBuffer){ 如果(!directBuffer.isDirect()){ 返回false; } 试一试{ 不安全。invokeCleaner(directBuffer); 返回true; }捕获(IllegalArgumentException ex){ 例如printStackTrace(); 返回f