Luk*_*son 2 java java-native-interface jvm
我在使用 JNI 时遇到了一个非常奇怪的问题。有人可以帮我理解这里有什么问题吗?
如果我按原样运行下面的代码,我会看到:
(a) 7fb6f022faf0 7fb6f022fb00 0
(b) 7fb6f022faf8 7fb6f022fb00 1
Run Code Online (Sandbox Code Playgroud)
如果我取消注释标记为 的行// (*),那么我会得到:
(a) 7f6ce822faf0 7f6ce822fb08 1
(b) 7f6ce822fb00 7f6ce822fb08 1
Run Code Online (Sandbox Code Playgroud)
(*)注释掉该行(这应该是无操作!)Integer.class后Integer.class,使用Class.equals方法发现 的一个实例与 的另一个实例不相等。取消注释该行,java.lang.Integer在test1方法中查找了两次而不是一次,由于某种原因,现在Integer.class发现两个实例是相等的!(这是在 JDK 16 上。)
有没有搞错?我完全不明白这...
pkg/Test.java:
package pkg;
public class Test {
public static native void test0();
public static native void test1(Object... args);
public static void main(String[] args) throws Exception {
test0();
test1(7);
}
}
Run Code Online (Sandbox Code Playgroud)
test.c:
#include <jni.h>
#include <stdio.h>
jclass Integer_class_0;
JNIEXPORT void JNICALL Java_pkg_Test_test0(JNIEnv *env, jclass ignored) {
Integer_class_0 = (*env)->FindClass(env, "java/lang/Integer");
}
JNIEXPORT void JNICALL Java_pkg_Test_test1(JNIEnv *env, jclass ignored,
jobjectArray args) {
//(*env)->FindClass(env, "java/lang/Integer"); // (*)
jobject arg = (*env)->GetObjectArrayElement(env, args, 0);
jclass arg_type = (*env)->GetObjectClass(env, arg);
jclass Integer_class_1 = (*env)->FindClass(env, "java/lang/Integer");
jclass cls_class = (*env)->FindClass(env, "java/lang/Class");
jmethodID cls_equals_methodID =
(*env)->GetMethodID(env, cls_class, "equals", "(Ljava/lang/Object;)Z");
printf("(a) %lx %lx %d\n", Integer_class_0, Integer_class_1,
(*env)->CallBooleanMethod(env,
Integer_class_0, cls_equals_methodID, Integer_class_1));
printf("(b) %lx %lx %d\n", arg_type, Integer_class_1,
(*env)->CallBooleanMethod(env,
arg_type, cls_equals_methodID, Integer_class_1));
}
Run Code Online (Sandbox Code Playgroud)
注意JNI 规范:
JNI 将本机代码使用的对象引用分为两类:局部引用和全局引用。本地引用在本地方法调用期间有效,并在本地方法返回后自动释放。全局引用在被显式释放之前一直有效。
对象作为本地引用传递给本地方法。JNI 函数返回的所有 Java 对象都是本地引用。JNI 允许程序员从本地引用创建全局引用。期望 Java 对象的 JNI 函数接受全局和局部引用。本机方法可能会返回对 VM 的本地或全局引用作为其结果。
您从方法调用中获取本地引用并尝试(*env)->FindClass(env, "java/lang/Integer")在test0方法调用中使用它test1,尽管它在test0返回时已自动释放。
访问释放的内存或无效的引用可能会产生任意影响,包括明显不相关的操作改变结果的可能性。
要获得在方法调用之间持续存在的引用,您必须使用NewGlobalRef.
顺便说一句,您可以使用IsSameObject来比较引用,而无需调用equals(重写时会有不同的语义)。
| 归档时间: |
|
| 查看次数: |
69 次 |
| 最近记录: |