使用调用 API 的 JNI 内存管理
当我使用 JNI 方法构建一个 java 对象时,为了将它作为参数传递给我使用 JNI 调用 API 调用的 java 方法,我如何管理它的内存?
When I'm building a java object using JNI methods, in order to pass it in as a parameter to a java method I'm invoking using the JNI invocation API, how do I manage its memory?
这是我的工作:
我有一个 C 对象,它有一个比 free()
更复杂的析构方法.这个 C 对象要与一个 Java 对象关联,一旦应用程序完成了 Java 对象,我就不再需要 C 对象了.
I have a C object that has a destructor method that is more complex that free()
. This C object is to be associated with a Java object, and once the application is finished with the Java object, I have no more need for the C object.
我正在像这样创建 Java 对象(为清楚起见,省略了错误检查):
I am creating the Java object like so (error checking elided for clarity):
c_object = c_object_create ();
class = (*env)->FindClass (env, "my.class.name");
constructor = (*env)->GetMethodID (env, class, "<init>", "(J)V");
instance = (*env)->NewObject (env, class, constructor, (jlong) c_object);
method = (*env)->GetMethodID (env, other_class, "doSomeWork", "(Lmy.class.name)V");
(*env)->CallVoidMethod (env, other_class, method, instance);
那么,既然我已经完成了 instance
,我该怎么处理它呢?理想情况下,我想把垃圾收集留给虚拟机;当它使用 instance
完成时,如果它还在我提供给它的指针上调用 c_object_destroy()
那就太棒了.这可能吗?
So, now that I'm done with instance
, what do I do with it? Ideally, I'd like to leave the garbage collection up to the VM; when it's done with instance
it would be fantastic if it also called c_object_destroy()
on the pointer I provided to it. Is this possible?
一个单独但相关的问题与我在这样的方法中创建的 Java 实体的范围有关;我是否必须手动释放,比如上面的 class
、constructor
或 method
?JNI 文档在正确的内存管理方面含糊不清(在我看来).
A separate, but related question has to do with the scope of Java entities that I create in a method like this; do I have to manually release, say, class
, constructor
, or method
above? The JNI doc is frustratingly vague (in my judgement) on the subject of proper memory management.
推荐答案
回收原生资源(对象、文件描述符等)有几种策略
There are a couple of strategies for reclaiming native resources (objects, file descriptors, etc.)
在 finalize() 期间调用 JNI 方法来释放资源.有些人建议不要实现finalize,基本上你不能确定你的原生资源永远被释放.对于内存等资源,这可能不是问题,但如果您有一个文件需要在可预测的时间刷新,finalize() 可能不是一个好主意.
Invoke a JNI method during finalize() which frees the resource. Some people recommend against implementing finalize, and basically you can't really be sure that your native resource is ever freed. For resources such as memory this is probably not a problem, but if you have a file for example which needs to be flushed at a predictable time, finalize() probably not a good idea.
手动调用清理方法.如果您在某个时间点知道必须清理资源,这将很有用.当我有一个必须在卸载 JNI 代码中的 DLL 之前释放的资源时,我使用了这种方法.为了允许稍后重新加载 DLL,在尝试卸载 DLL 之前,我必须确保对象确实被释放.仅使用 finalize(),我不会得到这个保证.这可以与 (1) 结合使用,以允许在 finalize() 期间或在手动调用的清理方法中分配资源.(您可能需要 WeakReferences 的规范映射来跟踪哪些对象需要调用其清理方法.)
Manually invoke a cleanup method. This is useful if you have a point in time where you know that the resource must be cleaned up. I used this method when I had a resource which had to be deallocated before unloading a DLL in the JNI code. In order to allow the DLL to later be reloaded, I had to be sure that the object was really deallocated before attempting to unload the DLL. Using only finalize(), I would not have gotten this guaranteed. This can be combined with (1) to allow the resource to be allocated either during finalize() or at the manually called cleanup method. (You probably need a canonical map of WeakReferences to track which objects needs to have their cleanup method invoked.)
据说PhantomReference也可以用来解决这个问题,但我不确定这样的解决方案究竟是如何工作的.
Supposedly the PhantomReference can be used to solve this problem as well, but I'm not sure exactly how such a solution would work.
实际上,我不得不在 JNI 文档上不同意你的观点.我发现 JNI 规范异常大多数重要问题都清楚,即使有关管理本地和全局引用的部分可以更详细地阐述.
Actually, I have to disagree with you on the JNI documentation. I find the JNI specification exceptionally clear on most of the important issues, even if the sections on managing local and global references could have been more elaborated.
相关文章