英文:
Making an object reference null from JNI code
问题
以下是你提供的内容的翻译:
这是我的问题的抽象描述。我想要在一个JNI函数中将Java代码中的对象引用设置为null。例如:
// -- java代码 --
String s="this is a new string";
func(s); //将对象传递给JNI函数。
System.out.println(s);
这应该打印出 null
首先,我将该对象作为jobject传递到JNI函数中,然后使用DeleteGlobalRef来移除引用。以下是JNI函数的代码。
JNIEXPORT void JNICALL
Java_func_test(JNIEnv *env, jobject this, jobject todel)
{
(*env)->DeleteGlobalRef(env,todel);
}
这会导致Java虚拟机崩溃,并显示以下消息。
> 要抑制以下错误报告,请在-XX:后面指定此参数
> 或在.hotspotrc中指定:SuppressErrorAt=/oopStorage.cpp:697
>
> Java运行时环境检测到致命错误:
>
> 内部错误(src/hotspot/share/gc/shared/oopStorage.cpp:697),pid=59362,tid=59363
> 断言(block != __null)失败:JNI全局:无效的释放0x00007f66ed673788
>
> JRE版本:OpenJDK Runtime Environment(14.0)(slowdebug版本14-internal+0-adhoc.manavjeet.jdk14)
> Java VM:OpenJDK 64位服务器VM(slowdebug版本14-internal+0-adhoc.manavjeet.jdk14,混合模式,分层,压缩
> 对象,g1 gc,linux-amd64)
> 有问题的框架:
> V [libjvm.so+0xf684eb] OopStorage::release(oopDesc* 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 正在转储核心... fish:“./java-slowdebug helloworld”被SIGABRT信号终止
通过这个情况,我理解这是非常错误的。请问是否有人可以指出错误,并建议在Java中将对象引用设置为null的正确方法。
英文:
Here is an abstraction of my problem. I want to set an object reference from the java code to null in a JNI function. For example:
// -- java code --
String s="this is a new string";
func(s); //passing the object to the JNI function.
System.out.println(s);
This should print null
Firstly, I am passing that object as jobject in the JNI function and then using DeleteGlobalRef to remove the reference. The JNI function is given below.
JNIEXPORT void JNICALL
Java_func_test(JNIEnv *env, jobject this, jobject todel)
{
(*env)->DeleteGlobalRef(env,todel);
}
This makes the JVM to crash with the following message.
> 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)
By this I understand this is horribly wrong. Can anyone please point out the mistake and suggest the correct way to set the object reference in java to null.
答案1
得分: 3
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; }
// 正如我所说;本机方法不是魔法
// 用JNI重写func不会使它更加强大;只会使它更加难以理解
void test() {
var s = new Box<>("this is a new string");
func(s);
System.out.println(s); // "Box(0: null)";
}
但...为什么要这样做呢?
英文:
Java is pass by value. The call func(s)
copies the reference s
and gives it to func
, which cannot change the original. null
is not an object; it's a special reference. That means func
can't set s
to null
, because it can't change s
, only the object behind it. Just being a native method does not give func
magical powers to break the rules of the language. The JNI is mostly for interfacing with functionality outside the language. Unless you find a way to peer into the caller's bytecode and mutate its stack frame, you can't do this.
Also, DeleteGlobalRef
does not work this way. It can only free references made by NewGlobalRef
, which you may call to remember state between calls to native functions. todel
here is a local reference, created anew for each call to the native method. I believe you could DeleteLocalRef
it, but that would not do anything.
<!-- language: c -->
JNIEXPORT void JNICALL
Java_func_test(JNIEnv *env, jobject this, jobject todel)
{
(*env)->DeleteLocalRef(env, todel);
}
This should at least not crash, but it still can't modify the reference the caller has; it's basically equivalent to
<!-- language: java -->
void func(Object todel) {
todel = null;
}
which does nothing. Again: Java copies the reference upon the call to func
. If the caller has their own copy of the reference you're trying to delete, no matter how hard you try to delete your copy of the reference, the caller's copy just won't change.
If you really, really wanted to, you could box the reference into another object and mutate that:
<!-- language: java -->
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)"
}
but... why would you?
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论