Утечка памяти при вызове Java-кода из C с использованием JNI - PullRequest
7 голосов
/ 27 августа 2009

У меня есть программа на C, которая хранит некоторый объект в хранилище java, используя JNI. (Прежде чем кто-то спросит, использование java store является обязательным требованием, и мне нужно написать клиент на C, который сможет добавлять и извлекать объекты из этого хранилища).

Я сделал программу и попытался добавить 100000 объектов размером 1 КБ. Но после добавления только 50000 объектов я получаю сообщения «из памяти» (обратите внимание, что я печатаю эти сообщения «из памяти» всякий раз, когда я не могу выделить новую строку или массив байтов с помощью функций NewStringUTF и NewByteArray). В то время мое приложение использует только 80 МБ памяти. Я не понимаю, почему эти методы возвращают NULL. Есть что-то, чего мне не хватает.

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

Вот исходный код.

    void create_jvm(void)
{
    JavaVMInitArgs vm_args;     
    JavaVMOption vm_options;

    vm_options.optionString = "-Djava.class.path=c:\\Store";
    vm_args.version = JNI_VERSION_1_4;
    vm_args.nOptions = 1;
    vm_args.options = &vm_options;
    vm_args.ignoreUnrecognized = 0;

    JNI_CreateJavaVM(&jvm, (void**)&env, &vm_args);

    if(env != null)
    {
        j_store = (*env)->FindClass(env, "com/store");
        if(j_store == null)
        {
            printf("unable to find class. class name: JStore");
        }       
    }   
}

void add(char* key, char* value, int length)
{
    jstring j_key = (*env)->NewStringUTF(env, key);
    jbyteArray j_value = (*env)->NewByteArray(env, length);

    (*env)->SetByteArrayRegion(env, j_value, 0, length, (jbyte *)value);
    ret = (*env)->CallStaticBooleanMethod(env, j_store, method_id, j_key, j_value);

    if(j_value != null)
    {
        (*env)->ReleaseByteArrayElements(env, j_value, (jbyte *)value, 0);
    }
    if(j_key != null)
    {
        (*env)->ReleaseStringUTFChars(env, j_key, key);
    }
}

Java-сторона получает данные в байте [] и сохраняет их в хеш-таблице. Проблема в том, что при каждом запуске кода память только складывается и никогда не освобождается. Я попытался добавить объект размером 1 МБ и отладил его.

Память процесса увеличивается на 1 МБ, когда я вызываю NewByteArray. Но когда вызывается CallStaticBooleanMethod, память процесса увеличивается на 4 МБ. И вызов ReleaseByteArrayElements вообще не освобождает память.

Если после этого я добавлю еще один объект размером 1 МБ, то память процесса останется такой же, когда я вызываю NewByteArray, и увеличится на 1 МБ при вызове CallStaticBooleanMethod, но останется такой же, когда я попытаюсь освободить массив байтов.

Ответы [ 4 ]

17 голосов
/ 08 сентября 2009

Когда вы вызываете функции New ..., вы создаете «локальную ссылку» - ссылку на этот объект в рамке локального стека. Это предотвращает Java VM от GC этого объекта, пока он вам все еще нужен. Это хорошо, если вы реализуете какой-то собственный метод - его локальный фрейм создается только на время вызова метода. Но когда вы создаете объект из собственного потока, присоединенного к Java, он привязывается к этому фрейму стека потоков, который будет уничтожен только этим потоком.

Итак, когда вы закончили с объектом, вы можете вызвать DeleteLocalRef (), чтобы сказать, что он вам больше не нужен. Или вы можете окружить всю функцию add () с помощью пары PushLocalFrame () / PopLocalFrame (), чтобы создать отдельный локальный кадр на всю его продолжительность.

4 голосов
/ 27 августа 2009

Целью функций ReleaseByteArrayElements и ReleaseStringUTFChars является не удаление объекта, а его разблокировка после получения указателя с помощью GetByteArrayElements или GetStringUTFChars. Два оператора if должны быть удалены.

0 голосов
/ 10 апреля 2013

Я пытался, вы все сказали.

необходимо использовать метод DeleteLocalRef после создания любой jstring и больше не использовать.

Newxxx или CallStaticObjectMethod могут создавать jstring, все они должны быть удалены.

0 голосов
/ 11 августа 2010

Да, я встречал ту же проблему.

Мое java-приложение вызывает JNI-приложение C ++, приложение C ++ запускает новый поток и вызывает метод java. В новом потоке было создано много объектов, и память быстро увеличивалась, хотя я использовал DeleteLocalRef, PushLocalFrame и PopLocalFram.

Я обнаружил, что многие объекты, созданные методом NewObject, не могут быть освобождены. Это странно .

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