JNI Присоединение / Отключение управления памятью потока - PullRequest
12 голосов
/ 10 марта 2012

У меня есть обратный вызов JNI:

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

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

Когда я запускаю его так (пустой полезный код), я получаю утечку памяти.Если я закомментирую весь метод, утечки нет.Как правильно прикреплять / отсоединять потоки?

Мое приложение обрабатывает звуковые данные в реальном времени, поэтому потоки, отвечающие за обработку данных, должны быть выполнены как можно скорее, чтобы быть готовыми к другому пакету.Поэтому для этих обратных вызовов я создаю новые темы.Каждую секунду их десятки или даже сотни, они присоединяются к JVM, вызывают функцию обратного вызова, которая перерисовывает граф, отсоединяет и умирает.Это правильный способ сделать это?Как справиться с утечкой памяти?

РЕДАКТИРОВАТЬ: опечатка

ОК Я создал необходимый минимальный код:

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

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

Ответы [ 2 ]

15 голосов
/ 10 марта 2012

Несколько замечаний по поводу обратного вызова в Java из собственного кода:

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

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

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

7 голосов
/ 28 мая 2013

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...