从 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)"
}
但是……你为什么要这么做?
这是我的问题的抽象。我想在 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)"
}
但是……你为什么要这么做?