gpt4 book ai didi

java - 在另一个 JNI 函数中使用时 Oop 被破坏

转载 作者:行者123 更新时间:2023-11-29 07:24:40 25 4
gpt4 key购买 nike

问题是我们可以在不同的 JNI 方法调用中缓存 jclassjmethodID 吗?

当我尝试使用来自另一个 JNI 方法调用的缓存 jclassjmethodID 创建某个特定类的对象时,我遇到了一些奇怪的行为。

这是一个简单的例子:

public class Main {
static {
System.loadLibrary("test-crash");
}

public static void main(String args[]) throws InterruptedException {
Thread.sleep(20000);
doAnotherAction(doSomeAction());
}

private static native long doSomeAction();

private static native void doAnotherAction(long ptr);
}

public class MyClass {
public int a;

public MyClass(int a) {
if(a == 10){
throw new IllegalArgumentException("a == 10");
}
this.a = a;
}
}

JNI 函数所做的只是创建类 MyClass 的对象。函数 doSomeAction 返回一个指向缓存的 jclassjmethodID 的指针。下面是本地方法的实现:

struct test{
jclass mc;
jmethodID ctor;
};

JNIEXPORT jlong JNICALL Java_com_test_Main_doSomeAction
(JNIEnv *env, jclass jc){
(void) jc;

jclass mc = (*env)->FindClass(env, "com/test/MyClass");
jmethodID ctor = (*env)->GetMethodID(env, mc, "<init>", "(I)V");

struct test *test_ptr = malloc(sizeof *test_ptr);
test_ptr->mc = mc;
test_ptr->ctor = ctor;

printf("Creating element0\n");
jobject ae1 = (*env)->NewObject(env, test_ptr->mc, test_ptr->ctor, (jint) 0);
(void) ae1;

printf("Creating element0\n");
jobject ae2 = (*env)->NewObject(env, test_ptr->mc, test_ptr->ctor, (jint) 0);
(void) ae2;

printf("Creating element0\n");
jobject ae3 = (*env)->NewObject(env, test_ptr->mc, test_ptr->ctor, (jint) 0);
(void) ae3;

return (intptr_t) test_ptr;
}

JNIEXPORT void JNICALL Java_com_test_Main_doAnotherAction
(JNIEnv *env, jclass jc, jlong ptr){
(void) jc;

struct test *test_ptr= (struct test *) ptr;
jclass mc = test_ptr->mc;
jmethodID ctor = test_ptr->ctor;

printf("Creating element\n");
jobject ae1 = (*env)->NewObject(env, mc, ctor, (jint) 0);
(void) ae1;

printf("Creating element\n");
jobject ae2 = (*env)->NewObject(env, mc, ctor, (jint) 0);
(void) ae2;

printf("Creating element\n");
jobject ae3 = (*env)->NewObject(env, mc, ctor, (jint) 0); //CRASH!!
(void) ae3;
}

问题是当试图在 Java_com_test_Main_doAnotherAction 中创建对象时取消引用 0 时程序崩溃。崩溃发生在 object_alloc 函数调用 java_lang_Class::as_Klass(oopDesc*) 时。

java_lang_Class::as_Klass(oopDesc*) 的分解是

Dump of assembler code for function _ZN15java_lang_Class8as_KlassEP7oopDesc:                                                                                                                                       
0x00007f7f6b02eeb0 <+0>: movsxd rax,DWORD PTR [rip+0x932ab5] # 0x7f7f6b96196c <_ZN15java_lang_Class13_klass_offsetE>
0x00007f7f6b02eeb7 <+7>: push rbp
0x00007f7f6b02eeb8 <+8>: mov rbp,rsp
0x00007f7f6b02eebb <+11>: pop rbp
0x00007f7f6b02eebc <+12>: mov rax,QWORD PTR [rdi+rax*1]
0x00007f7f6b02eec0 <+16>: ret

rdi 似乎包含指向相关Oop 的指针。我注意到前 5 次没有发生崩溃:

rdi            0x7191eb228

崩溃案例是

rdi            0x7191eb718

导致 0x0 返回并崩溃。

在不同的 JNI 函数中使用 jclassjmethodID 时,什么会导致 Oop 损坏?如果我使用本地找到的 jclassjmethodID 创建对象,一切正常。

UPD:在分析核心转储后,我发现 rdi 被加载为

mov    rdi,r13
#...
mov rdi,QWORD PTR [rdi]

虽然 r13 似乎没有在我的 JNI 函数中更新...

最佳答案

在 JNI 调用中缓存 jclass 是一个主要 ( though typical) 错误。
jclass 是一个 special case jobject - 它是一个 JNI 引用,应该被管理。

作为 JNI 规范 says , JNI 函数返回的所有 Java 对象都是本地引用。因此,FindClass 返回本地 JNI 引用,该引用在 native 方法返回后立即失效。也就是说,如果对象被移动,GC 将不会更新引用,或者另一个 JNI 调用可能会为不同的 JNI 引用重用相同的槽。

为了在 JNI 调用中缓存 jclass,您可以使用 NewGlobalRef 将其转换为全局引用。功能。

jthreadjstringjarrayjobjects的其他例子,它们也应该被管理。

JNIEnv* 也不能缓存,因为它仅有效 in the current thread .

同时 jmethodIDjfieldID 可以在 JNI 调用中安全地重用 - 它们明确地标识 JVM 中的方法/字段并且是 intended for repeated use只要 holder 类还活着。但是,如果 holder 类恰好被垃圾收集,它们也可能变得无效。

关于java - 在另一个 JNI 函数中使用时 Oop 被破坏,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56246852/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com