Search code examples
javajava-native-interface

JNI Attach/Detach thread memory management


I have a JNI Callback:

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

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

When I run it like this (empty useful code), I get a memory leak. If I comment out the whole method, there is no leak. What is the correct way of attaching / detaching threads?

My application processes real-time sound data, so the threads responsible for data processing must be done as soon as possible in order to be ready for another batch. So for these callbacks, I create new threads. There are dozens or even hundreds of them each second, they attach themselves to JVM, call a callback function which repaints a graph, detach and die. Is this a correct way of doing this stuff? How to handle the leaking memory?

EDIT: typo

OK I have created a mimimal code needed:

package test;

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

    public static native void start();
}

and

#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;
}

and when I run the VisualJM profiler, I get the usual sawtooth pattern, no leak there. Heap usage peaked at around 5MB. However, observing the process explorer indeed shows some weird behaviour: memory is slowly rising and rising, 4K a second for a minute or so and then suddenly all this allocated memory drops. These drops do not correspond with garbage collection (they occur less often and deallocate less memory than those saw-teeth in the profiler).

So my best bet is that it is some OS behaviour handling tens of thousands milisecond-lived threads. Does some guru have an explanation for this?


Solution

  • I figured the problem. It was dangling local references in the JNI code I didn't destroy. Every callback would create a new local reference, thus resulting in a memory leak. When I converted the local reference to global, so I could reuse it, the problem disappeared.