2012-03-09 1 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に接続し、グラフを再ペイントし、切り離して死ぬコールバック関数を呼び出します。これは正しいことでしょうか?漏れたメモリをどのように処理するのですか?

EDIT:タイプミス

私が必要mimimalコード作成しているOK:

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でピークに達しました。しかし、プロセスエクスプローラを実際に観察すると、メモリがゆっくりと立ち上がり、立ち上がり、1分ほどで4K秒後に突然この割り当てられたメモリがすべて低下します。これらのドロップは、ガベージコレクションには対応していません(プロファイラーのノコギリよりも少ない頻度で発生し、メモリの割り当てを減らします)。

だから、私の最善の策は、数万のmilisecond-livesスレッドを処理するいくつかのOSの動作です。いくつかの教祖はこれについて説明していますか?ネイティブコードから戻ってのJavaを呼び出す約

答えて

7

問題が発生しました。私が破壊しなかったJNIコードでは、ローカル参照がぶら下がっていました。すべてのコールバックで新しいローカル参照が作成され、メモリリークが発生します。ローカル参照をグローバルに変換すると、再利用できるようになり、問題は消えました。

14

いくつかの点:jvm-場合のみ

  • AttachCurrentThreadと呼ばれるべきである> GETENV()はゼロの値を返します。スレッドが既にアタッチされている場合は通常はノーオペレーションですが、オーバーヘッドを少しでも抑えることができます。
  • AttachCurrentThreadを呼び出した場合のみ、DetachCurrentThreadを呼び出す必要があります。
  • 将来同じスレッドで呼び出されることが予想される場合は、デタッチを避けてください。

ネイティブコードのスレッド動作に応じて、デタッチを避け、終了時にすべてのネイティブスレッドへの参照を保存することができます(これを行う必要がある場合でもアプリケーションに依存する可能性がありますクリーンアップするためのシャットダウン)。

ネイティブスレッドを継続的に接続して切り離す場合、VMは(多くの場合同じ)スレッドをJavaオブジェクトと継続的に関連付ける必要があります。一部のVMは、スレッドを再利用したり、パフォーマンスを向上させるためにマッピングを一時的にキャッシュしたりすることがありますが、VMに依存しない場合は、より良い予測可能な動作が得られます。

+0

私はあなたの意見を見て、私は通常同意するでしょう。私が作成しているスレッドは本当に短命です。私はそれらを(WinApi CreateThreadを使って)作成し、直ちにJVMに添付します。Javaでは、Swingグラフに新しい値を再描画します。それらが完了すると、それらは分離して実行を停止します(0を返します)。その時点で、それらはOSによって破壊されるべきです。それらはJavaに新しい値を渡すために使用されます。各サウンドチャンネルには1秒間に約40個のチャンネルがあります(私は最大32チャンネルを扱います)。 –

+0

スレッドプールを検討するとよいでしょう。新しいスレッドを継続的に生成するのではなく、スレッドを長くしてキューから入力を取得するようにします。これにより、OSとJavaの両方のスレッド管理オーバーヘッドが削減されます。 – technomage

+0

メソッドを呼び出すだけでなく、JNIでJavaのものを実行している場合、それらのアクションについてローカルフレームをプッシュ/ポップすることもできます。 – technomage

関連する問題