Удаление ссылок на рабочие места в API вызова JNI назначения объектов - PullRequest
0 голосов
/ 06 ноября 2018

Я хочу выполнить следующий цикл:

for(absolute_date CURRENT_DATE = START_DATE; CURRENT_DATE.COMPARE_TO(END_DATE) <= 0; CURRENT_DATE.SHIFT_SELF(TIMESTEP))
{
    CURRENT_STATE = Propagator.PROPAGATE(CURRENT_DATE);
}

Где CURRENT_STATE - объект класса state. propagator::PROPAGATE() - это метод, который возвращает объект класса state.

Мой класс state фактически является оберткой для класса библиотеки Java, который я вызываю через API вызова JNI. Проблема, с которой я столкнулся, заключается в том, что я хочу удалить локальные ссылки Java с помощью DeleteLocalRef, чтобы предотвратить утечки памяти (особенно важно, так как я буду зацикливаться много тысяч раз).

Однако, поскольку в моем деструкторе класса state вызывается DeleteLocalRef, ссылка на java jobject уничтожается при возврате RHS присвоения, что делает CURRENT_STATE недействительным, так как содержит ссылку на jobject, который был Исключен.

Как мне избежать этого?

@ Wheezil

Относительно вашего первого замечания - поскольку я использую API вызова, т.е. создаю виртуальную машину внутри C ++ и вызываю функции Java, я не думаю, что мне нужно преобразовывать в глобальные ссылки (поскольку все локальные ссылки остаются действительными до тех пор, пока JVM не будет уничтожен или пока не отсоединится нить). В этом случае я не отключаю и не присоединяю потоки к JVM, поэтому локальные ссылки никогда не удаляются. Для меня важно убедиться, что локальные ссылки удаляются в JVM.

Относительно второго пункта - я уже запретил копирование, установив мои конструкторы копирования / операторы присваивания = удалить. Моя проблема более конкретно о том, как обеспечить удаление этих ссылок.

Мой класс состояния выглядит так:

state::state(JNIEnv* ENV)
{
    this->ENV = ENV;
    this->jclass_state = ENV->FindClass("/path/to/class");
    this->jobject_state = nullptr;                                                  
}

state::~state()
{
    if(DOES_JVM_EXIST())
    {
        ENV->DeleteLocalRef(this->jclass_state); 
        ENV->DeleteLocalRef(this->jobject_state); //PROBLEMATIC
    }
}

state::state(state&& state_to_move)
{
    this->ENV = state_to_move.ENV;

    //move jobjects from mover => new object
    this->jobject_state = state_to_move.jobject_state;
    this->jclass_state = state_to_move.jclass_state;
}

state& state::operator =(state&& state_to_move)
{
    this->ENV = state_to_move.ENV;

    //move jobjects from mover => current object
    this->jobject_state= state_to_move.jobject_state;
    this->jclass_state = state_to_move.jclass_state;
    return *this;
} 

Чтобы описать проблему, с которой я сталкиваюсь, более подробно: метод propagator::PROPAGATE() возвращает объект state по значению (в настоящее время выделен стек). Как только эта функция возвращается, происходит следующее:

1) Оператор назначения перемещения вызывается. Это устанавливает элементы jobject_state и jclass_state в объекте CURRENT_STATE.

2) Деструктор вызывается для экземпляра state, созданного в функции PROPAGATE (). Это удаляет локальную ссылку на jobject_state и, таким образом, объект CURRENT_STATE больше не имеет допустимой переменной-члена.

Ответы [ 2 ]

0 голосов
/ 07 ноября 2018

Вы не можете сделать это:

this->ENV = ENV;

Вы кэшируете значение JNIEnv, передаваемое нативному коду из JVM.

Ты не можешь этого сделать.

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

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

Вы получаете JNIenv * каждый раз, когда нативный код вызывается из JVM, поэтому практически нет смысла кэшировать значение JNIEnv.

IMO, вы делаете свой собственный код слишком сложным. Нет необходимости кэшировать и отслеживать все эти ссылки. Похоже, вы пытаетесь синхронизировать нативный объект C ++ с Java-объектом. Зачем? Если нативный код нуждается в доступе к объекту Java, просто передайте этот объект при вызове из Java в собственный код.

0 голосов
/ 07 ноября 2018

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

struct state {
   state(jobject thing_) : thing(thing_) {}
   ~state() { env->DeleteLocalRef(thing); }
   jobject thing;
}

Первая проблема заключается в том, что хранение локальных ссылок опасно. Вы не можете висеть на них за пределами текущего кадра JNI. Так что конвертируйте их в глобальные:

struct state {
   state(jobject thing_) : thing(env->NewGlobalRef(thing_)) { 
      env->DeleteLocaLRef(thing_); 
   }
   ~state() { env->DeleteGlobalRef(thing); }
   jobject thing;
}

Вторая проблема заключается в том, что jobject в основном похож на старый C ++ auto_ptr <> - действительно небезопасно, поскольку его копирование приводит к опасным указателям и двойным освобождениям. Таким образом, вам нужно либо запретить копирование состояния и, возможно, только передать состояние *, либо создать конструктор копирования, который работает:

state(const state& rhs) thing(env->NewGlobalRef(rhs.thing)) {}

Это должно по крайней мере вывести вас на правильный путь.

ОБНОВЛЕНИЕ: Ddor, в отношении локальных и глобальных ссылок, эта ссылка хорошо описывает это: «Локальные ссылки становятся недействительными, когда выполнение возвращается из собственного метода, в котором создана локальная ссылка. Следовательно, собственная ссылка Метод не должен хранить локальную ссылку и ожидать ее повторного использования в последующих вызовах. " Вы можете хранить местные ссылки, но только при строгих обстоятельствах. Обратите внимание, что, в частности, вы не можете передать их другому потоку, что, по-видимому, вы не делаете. Другое дело - существуют ограничения на общее количество локальных ссылок, которые могут быть активными. Этот предел разочаровывает, но он кажется специфичным для JVM. Я советую осторожность и всегда переходить на глобальный.

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

ENV->DeleteLocalRef(this->jclass_state); 

Я должен признать незнание семантики перемещения C ++; просто убедитесь, что копия по умолчанию ctor не вызывается и ваше jobject_state не освобождается дважды.

this->jobject_state = state_to_move.jobject_state;

Если ваш конструктор перемещения вызывается вместо конструктора копирования или присваивания, я не знаю, почему вы увидите удаление при уничтожении временного объекта. Как я уже сказал, я не эксперт по семантике перемещения. У меня всегда был конструктор копирования, создающий новый глобал. ссылка.

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