JNI附加/分离线程内存管理

我有一个JNI回调:

void callback(Data *data, char *callbackName){ JNIEnv *env; jvm->AttachCurrentThread((void **)&env, NULL); /* start useful code*/ /* end useful code */ jvm->DetachCurrentThread(); } 

当我这样运行它(空的有用代码)时,我得到了内存泄漏。 如果我注释掉整个方法,就没有泄漏。 连接/分离线程的正确方法是什么?

我的应用程序处理实时声音数据,因此必须尽快完成负责数据处理的线程,以便为另一批次做好准备。 所以对于这些回调,我创建了新的线程。 它们每秒都有几十甚至几百个,它们将自己附加到JVM,调用一个重新绘制图形,分离和死亡的回调函数。 这是做这件事的正确方法吗? 如何处理泄漏的内存?

编辑:错字

好的,我已经创建了一个所需的mimimal代码:

 package test; public class Start { public static void main(String[] args) throws InterruptedException{ System.loadLibrary("Debug/JNITest"); start(); } public static native void start(); } 

 #include  #include  #include "test_Start.h" JavaVM *jvm; DWORD WINAPI attach(__in LPVOID lpParameter); JNIEXPORT void JNICALL Java_test_Start_start(JNIEnv *env, jclass){ env->GetJavaVM(&jvm); while(true){ CreateThread(NULL, 0, &(attach), NULL, 0, NULL); Sleep(10); } } DWORD WINAPI attach(__in LPVOID lpParameter){ JNIEnv *env; jvm->AttachCurrentThread((void **)&env, NULL); jvm->DetachCurrentThread(); return 0; } 

当我运行VisualJM探查器时,我得到了通常的锯齿图案,那里没有泄漏。 堆使用率达到5MB左右。 然而,观察进程资源管理器确实显示出一些奇怪的行为:内存缓慢上升和上升,4K一秒钟一分钟左右然后突然所有这些分配的内存下降。 这些下落与垃圾收集不对应(它们发生的频率较低,并且比分析器中的锯齿更少释放内存)。

所以我最好的选择是,它是一些处理成千上万毫秒生命线程的操作系统行为。 有些大师对此有解释吗?

关于从本机代码回调到Java的几点:

  • 只有在jvm-> GetEnv()返回零值时才应调用AttachCurrentThread。 如果线程已经连接,它通常是一个无操作,但你可以节省一些开销。
  • 只有在调用AttachCurrentThread时才应调用DetachCurrentThread。
  • 如果您希望将来在同一个线程上调用,请避免分离。

根据您的本机代码的线程行为,您可能希望避免分离,而是存储对所有本机线程的引用以便在终止时进行处理(如果您甚至需要这样做;您可以依赖应用程序关闭来清理)。

如果您不断地附加和分离本机线程,则VM必须不断地将(通常是相同的)线程与Java对象相关联。 某些虚拟机可能会重新使用线程,或临时缓存映射以提高性能,但如果您不依赖虚拟机为您执行此操作,您将获得更好,更可预测的行为。

我想出了问题。 它是悬挂在我没有销毁的JNI代码中的本地引用。 每个回调都会创建一个新的本地引用,从而导致内存泄漏。 当我将本地引用转换为全局时,我可以重用它,问题就消失了。