2012-03-09 38 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