Создание нулевой ссылки на объект из кода JNI - PullRequest
0 голосов
/ 02 августа 2020

Вот абстракция моей проблемы. Я хочу установить ссылку на объект из кода java на null в функции JNI . Например:

// -- java code --
String s="this is a new string";
func(s);                        //passing the object to the JNI function.
System.out.println(s);

Это должно напечатать null

Во-первых, я передаю этот объект как объект задания в функции JNI, а затем использую DeleteGlobalRef , чтобы удалить ссылка. Функция JNI приведена ниже.

JNIEXPORT void JNICALL
Java_func_test(JNIEnv *env, jobject this, jobject todel)
{
    (*env)->DeleteGlobalRef(env,todel);
}

Это заставляет JVM обрабатывать sh со следующим сообщением.

Чтобы подавить следующий отчет об ошибке, укажите этот аргумент после -XX: или в .hotspotr c: SuppressErrorAt = / oopStorage. cpp: 697

Java Среда выполнения обнаружила фатальную ошибку:

Внутренняя ошибка ( src / hotspot / share / gc / shared / oopStorage. cpp: 697), pid = 59362, tid = 59363 assert (block! = __null) не удалось: JNI global: недопустимый выпуск 0x00007f66ed673788

Версия JRE: Среда выполнения OpenJDK (14.0) (slowdebug build 14-internal + 0-adho c .manavjeet.jdk14) Java VM: 64-разрядная серверная виртуальная машина OpenJDK (slowdebug 14-internal + 0-adho c .manavjeet. jdk14, смешанный режим, многоуровневый, сжатый oops, g1 g c, linux -amd64) Проблемы c кадр: V [libjvm.so + 0xf684eb] OopStorage :: release (oopDes c* const *) + 0x49

Будет записан дамп ядра. Расположение по умолчанию: дампы ядра могут обрабатываться с помощью "/ usr / share / apport / apport% p% s% c% d% P% E" (или дамп в /home/manavjeet----/core.59362)

Файл отчета об ошибке с дополнительной информацией сохраняется как: /home/manavjeet/---/hs_err_pid59362.log

Если вы хотите отправить отчет об ошибке, посетите: https://bugreport.java.com/bugreport/crash.jsp Текущий поток - 59363 Дамп ядра ... fi sh: «./java-slowdebug helloworld» завершается сигналом SIGABRT (Abort)

Этим я понимаю, что это ужасно неправильно. Может ли кто-нибудь указать на ошибку и предложить правильный способ установки ссылки на объект в java на null.

1 Ответ

3 голосов
/ 02 августа 2020

Java передается по значению. Вызов func(s) копирует ссылку s и передает ее func, который не может изменить оригинал. null не является объектом; это особая ссылка. Это означает, что func не может установить s в null, потому что он не может изменить s, только объект за ним. Просто быть родным методом не дает func магической силы нарушать правила языка. JNI в основном предназначен для взаимодействия с функциями за пределами языка. Если вы не найдете способ заглянуть в байт-код вызывающего абонента и изменить его кадр стека, вы не сможете этого сделать.

Кроме того, DeleteGlobalRef не работает таким образом. Он может только освобождать ссылки, сделанные NewGlobalRef, которые вы можете вызывать для запоминания состояния между вызовами собственных функций. todel вот локальная ссылка, созданная заново для каждого вызова собственного метода. Я полагаю, вы могли бы DeleteLocalRef это сделать, но это ничего не даст.

JNIEXPORT void JNICALL
Java_func_test(JNIEnv *env, jobject this, jobject todel)
{
    (*env)->DeleteLocalRef(env, todel);
}

Это должно по крайней мере не cra sh, но оно все равно не может изменить ссылку, которую имеет вызывающий; это в основном эквивалент

void func(Object todel) {
    todel = null;
}

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

Если вы действительно , действительно хотел, вы можете поместить ссылку в другой объект и изменить это:

public class Box<T> {
    public T ref;
    public Box() { this(null); }
    public Box(T ref) { this.ref = ref; }
    public String toString() {
        return "Box(" + System.identityHashCode(ref) + ": " + ref + ")";
    }
}

<T> void func(Box<T> box) { box.ref = null; }
// as I said; native methods are not magic
// rewriting func with the JNI does not make it more powerful; just more inscrutable

void test() {
    var s = new Box<>("this is a new string");
    func(s);
    System.out.println(s); // "Box(0: null)"
}

но ... зачем вам это?

...