强制GC收集JNI代理对象

时间:2010-03-09 22:11:09

标签: java-native-interface garbage-collection

虽然我在使用结束时尽力清理JNI对象以释放本机内存,但仍有一些问题长期存在,浪费系统本机内存。

有没有办法强制GC优先收集这些JNI代理?

我的意思是有没有办法让GC专注于某种特定的对象,即JNI代理?

感谢。

3 个答案:

答案 0 :(得分:2)

如果你在谈论在本机代码中分配的内存(以及扩展名,句柄),它不在JVM垃圾收集器的范围之内 - 它没有什么可以做的,所以你可以独立完成。如果你在完成后没有在本机代码中释放内存,它将会泄漏。

如果您指的是通过其访问本机代码的Java对象,则它们是完全正常的对象,当它们变得无法访问时将被收集。请注意,如果您使用本机代码固定Java对象(例如,使用GetByteArrayElements,则必须释放它们(例如,使用ReleaseByteArrayElements)。

如果您的本机代码必须在让Java对象运行之前释放资源,那么Java对象应该具有某种类型的dispose方法,该方法在调用时将释放本机资源并使Java对象无法进一步使用。只需调用dispose方法并让对象引用即可。

最后一件事,我知道一旦加载就无法卸载本机库。

答案 1 :(得分:1)

没有办法让GC“专注”某些类型的对象。我假设您在终结器中清理,并在以下时间运行终结器:

  • 无法再访问该对象。
  • GC决定清理JNI代理所在的代。

这意味着,为了尽可能快地清理资源,您需要:

  • 缩小参考范围,因此您的程序不会在不必要的长时间内依赖它们。此外,垃圾收集很少用于旧对象,因此有一个双重理由确保它们尽可能短的时间。
  • 添加一个手动清理方法,客户端代码在完成JNI代理后可以调用 - 不要让引用消失并等待终结器运行。

示例:

class NativeResource {
    private static native long allocate();
    private static native void release(long handle);
    private final long handle;
    private boolean closed = false;
    public NativeResource(){
        handle = allocate();
    }
    /** Deallocates the native resources associated with this proxy. */
    public void close() { 
        if (closed) throw new IllegalStateException("Already closed");
        release(handle); 
        closed = true;
    }
    protected void finalize() throws Throwable {
        try { 
            if (!closed) release(handle);
        } finally {
            super.finalize();
        }
    }
}

// Usage:
NativeResource nr = new NativeResource();
try {
    // Use the resource for something
} finally {
    nr.close(); // Make sure resource is closed even after exceptions
}

答案 2 :(得分:1)

你的GC心理模型是错误的。 GC不会收集对象然后释放它们。

GC收集实时对象。然后将所有其他内存定义为空闲。

对于具有终结器的对象,以及可能在堆栈上分配的对象的优化等,对此有皱纹,但这是正确的心理模型。

全局引用(通过JNI提供的持久引用形式)充当对象的根。 GC从根开始,并在查找活动对象时递归跟随所有链接。如果删除全局引用,则它将停止使引用的对象保持活动状态。然后,GC可以回收对象使用的内存,但仅在没有任何其他引用的情况下,并且仅在后续收集期间回收。没有通用的方法来回收任何特定对象子集的内存。