JNI Attach/Detach 线程内存管理

2022-01-25 00:00:00 java java-native-interface

我有一个 JNI 回调:

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?

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

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?

错字

好的,我已经创建了所需的最小代码:

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();
}

#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 持续一分钟左右,然后突然所有分配的内存都下降了.这些丢弃与垃圾回收不对应(与分析器中的锯齿相比,它们发生的频率更低,释放的内存也更少).

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?

推荐答案

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

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.

相关文章