2012-03-09 26 views
12

我有一个JNI回调:JNI连接/断开线程内存管理

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

    /* end useful code */ 
    jvm->DetachCurrentThread(); 
} 

当我运行像这样(空有用的代码),我得到了内存泄漏。如果我评论整个方法,就没有泄漏。什么是附加/分离线程的正确方法?

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

编辑:错字

OK我已经创建需要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 <jni.h> 
#include <Windows.h> 
#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

回答

7

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

14

几点:)

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

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

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

+0

我明白你的意思,我通常会同意。我创建的线程真的很短暂。我创建它们(使用WinApi CreateThread),然后立即将它们附加到JVM。在Java中,他们使用新值重绘Swing图形。当它们完成时,它们分离并停止执行(返回0),此时它们应该被操作系统销毁。它们被用来为Java传递一个新的值。每个声道每秒约有40个声道(我可以使用多达32个声道)。 – 2012-03-10 16:31:17

+0

你可能会考虑线程池。让你的线程停留更长时间并从队列中接受输入,而不是不断地产生新的线程。这将减少OS和Java中的线程管理开销。 – technomage 2012-03-12 15:23:30

+0

如果除了调用方法之外,您在JNI中执行任何Java内容,您可能还希望围绕这些操作推送/弹出本地框架。 – technomage 2012-03-12 15:24:37