Подтвердить что ты не робот

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: typo

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, я получаю обычный пилообразный рисунок, без утечки. Использование кучи достигло максимума около 5 МБ. Тем не менее, наблюдение за проводником процессов действительно показывает какое-то странное поведение: память медленно поднимается и поднимается, 4 К в секунду в течение минуты или около того, а затем внезапно все это выделяет память. Эти капли не соответствуют сбору мусора (они встречаются реже и освобождают меньше памяти, чем пильные диски в профилировщике).

Так что я лучше всего то, что это некоторая работа с ОС, обрабатывающая десятки тысяч milisecond-живых потоков. У кого-то у гуру есть объяснение?

4b9b3361

Ответ 1

Я решил проблему. Это были болтающиеся локальные ссылки в коде JNI, который я не уничтожал. Каждый обратный вызов создавал бы новую локальную ссылку, что приводило бы к утечке памяти. Когда я преобразовал локальную ссылку в глобальную, я могу ее повторно использовать, проблема исчезла.

Ответ 2

Несколько вопросов о переходе на Java из собственного кода:

  • AttachCurrentThread следует вызывать только в том случае, если jvm- > GetEnv() возвращает нулевое значение. Обычно он не работает, если поток уже подключен, но вы можете сэкономить некоторые накладные расходы.
  • DetachCurrentThread следует вызывать только при вызове AttachCurrentThread.
  • Избегайте отсоединения, если вы ожидаете, что в будущем вы будете вызваны в тот же поток.

В зависимости от поведения нативного кода вашего кода вы можете избежать отсоединения и вместо этого хранить ссылки на все собственные потоки для удаления при завершении (если вам это нужно, вы можете полагаться на выключение приложения для очистки вверх).

Если вы постоянно прикрепляете и отключаете собственные потоки, виртуальная машина должна постоянно связывать (часто одни и те же) потоки с объектами Java. Некоторые виртуальные машины могут повторно использовать потоки или временно кэшировать сопоставления для повышения производительности, но вы получите лучшее и более предсказуемое поведение, если вы не будете полагаться на виртуальную машину, чтобы сделать это за вас.