强制GC收集JNI代理对象

3

虽然我尽力清理JNI对象以释放使用结束时的本地内存,但仍有一些对象会在系统本地内存中占用很长时间,浪费资源。

有没有办法强制GC优先回收这些JNI代理对象呢?

我的意思是,是否有一种方法可以使GC集中处理某种特定类型的对象,即JNI代理对象?

谢谢。


如果您尽力清理JNI对象,但仍有一些“挂在那里”,那么您的代码编写得很糟糕,会导致内存泄漏。您必须通过调用DeleteLocalRef()或DeleteGlobalRef()来释放它们中的所有对象,这样就不会有任何东西“挂在那里”了。我建议将所有jobject和jstring都包装到一个包装类中,在其析构函数中销毁它们。这是避免C++内存泄漏的唯一可靠方法。 - Elmue
3个回答

3
如果您谈到的是在本机代码中分配的内存(以及通过此获得的句柄),那么它位于 JVM 垃圾回收器监管范围之外 - 它无法对其进行操作,因此您需要自己处理。如果您在完成时不释放本机代码中的内存,则会发生泄漏。
如果您指的是通过 Java 对象访问本机代码,那么这些对象是完全正常的对象,当它们变得不可访问时将被收集。请注意,如果您在本机代码中固定 Java 对象(例如使用 "GetByteArrayElements"),则还必须释放它们(例如使用 "ReleaseByteArrayElements")。
如果您的本机代码必须在让 Java 对象继续使用之前释放资源,则该 Java 对象应该有某种 dispose 方法,调用该方法将释放本机资源并使 Java 对象无法再使用。只需调用 dispose 方法,然后让对象引用去吧。
最后要说一件事,我不知道有什么方法可以卸载已加载的本机库。

嗨。我知道这个,只是想知道是否有一种方法可以强制Java收集指向本地对象的指针。 - SyBer
1
我不明白你的意思 - 如果你“知道这一点”,即本地代码分配的内存超出了Java垃圾收集器的控制范围,那么你为什么要问垃圾收集器是否可以处理指向本地分配内存的指针(这就是本地对象,并且垃圾收集器对此一无所知)? - Lawrence Dol
你好。问题是是否有一种方法可以使GC集中处理特定类型的对象,即JNI代理。谢谢。 - SyBer

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
}

1
你对垃圾回收(GC)的心理模型是错误的。GC不会收集对象然后释放它们。
GC只收集活动对象。所有其他内存都被定义为自由内存。
这对于具有finalizers的对象存在一些问题,以及针对可能在堆栈上分配的对象的优化,但这是正确的心理模型。
全局引用(通过JNI可用的持久引用形式)作为对象的根。GC从根开始,并递归地跟随所有链接以查找活动对象。如果删除全局引用,则它将停止保持所引用对象的活动状态。然后,GC可能会回收对象使用的内存,但仅当没有其他引用时,且仅在随后的收集期间进行。没有通用的方法来回收任何特定子集的对象的内存。

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