从 JNI 代码中使对象引用为 null

Making an object reference null from JNI code

这是我的问题的抽象。我想在 JNI 函数 中将来自 java 代码的对象引用设置为 null 。例如:

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

这应该打印 null

首先,我将该对象作为 JNI 函数中的 jobject 传递,然后使用 DeleteGlobalRef 删除引用。下面给出JNI函数。

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

这会使 JVM 崩溃并显示以下消息。

To suppress the following error report, specify this argument after -XX: or in .hotspotrc: SuppressErrorAt=/oopStorage.cpp:697

A fatal error has been detected by the Java Runtime Environment:

Internal Error (src/hotspot/share/gc/shared/oopStorage.cpp:697), pid=59362, tid=59363 assert(block != __null) failed: JNI global: invalid release 0x00007f66ed673788

JRE version: OpenJDK Runtime Environment (14.0) (slowdebug build 14-internal+0-adhoc.manavjeet.jdk14) Java VM: OpenJDK 64-Bit Server VM (slowdebug 14-internal+0-adhoc.manavjeet.jdk14, mixed mode, tiered, compressed oops, g1 gc, linux-amd64) Problematic frame: V [libjvm.so+0xf684eb] OopStorage::release(oopDesc* const*)+0x49

Core dump will be written. Default location: Core dumps may be processed with "/usr/share/apport/apport %p %s %c %d %P %E" (or dumping to /home/manavjeet----/core.59362)

An error report file with more information is saved as: /home/manavjeet/---/hs_err_pid59362.log

If you would like to submit a bug report, please visit: https://bugreport.java.com/bugreport/crash.jsp Current thread is 59363 Dumping core ... fish: “./java-slowdebug helloworld” terminated by signal SIGABRT (Abort)

由此我明白这是非常错误的。任何人都可以指出错误并建议将 java 中的对象引用设置为空的正确方法。

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);
}

这至少应该不会崩溃,但它仍然不能修改调用者拥有的引用;基本上等同于

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)"
}

但是……你为什么要这么做?