Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/313.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 在另一个JNI函数中使用时,Oop会损坏_Java_Jvm_Java Native Interface_Jvm Hotspot - Fatal编程技术网

Java 在另一个JNI函数中使用时,Oop会损坏

Java 在另一个JNI函数中使用时,Oop会损坏,java,jvm,java-native-interface,jvm-hotspot,Java,Jvm,Java Native Interface,Jvm Hotspot,问题是我们能否跨不同的JNI方法调用缓存jclass和JMethodi 当我试图通过另一个JNI方法调用使用缓存的jclass和JMethodi创建某个特定类的对象时,我遇到了一些奇怪的行为 下面是一个简单的例子: public class Main { static { System.loadLibrary("test-crash"); } public static void main(String args[]) throws Interrupte

问题是我们能否跨不同的JNI方法调用缓存jclass和JMethodi

当我试图通过另一个JNI方法调用使用缓存的jclass和JMethodi创建某个特定类的对象时,我遇到了一些奇怪的行为

下面是一个简单的例子:

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返回一个指向缓存的jclass和jmethodID的指针。以下是本机方法的实现:

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;
}
rdi在这里似乎包含一个指向相关Oop的指针。我注意到的是前5次没有发生碰撞:

rdi            0x7191eb228
坠机事件是

rdi            0x7191eb718
导致返回0x0并崩溃

当跨不同的JNI函数使用jclass和JMethod时,Oop会损坏什么?如果我使用本地找到的jclass和JMethod创建对象,那么一切都很好

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

mov    rdi,r13
#...
mov    rdi,QWORD PTR [rdi]
虽然r13似乎没有在我的JNI函数中更新…

跨JNI调用缓存jclass是一个重大错误。 jclass是jobject的一个组件-它是一个JNI引用,应该进行管理

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

为了跨JNI调用缓存jclass,可以使用函数将其转换为全局引用

jthread、jstring、jarray是作业对象的其他示例,它们也应该被管理

JNIEnv*也不能被缓存,因为它仅有效

同时,jmethodID和jfieldID可以跨JNI调用安全地重用——它们明确地标识JVM中的方法/字段,并且只要holder类还活着就可以使用。但是,如果holder类恰好被垃圾收集,它们也可能变得无效。

跨JNI调用缓存jclass是一个重大错误。 jclass是jobject的一个组件-它是一个JNI引用,应该进行管理

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

为了跨JNI调用缓存jclass,可以使用函数将其转换为全局引用

jthread、jstring、jarray是作业对象的其他示例,它们也应该被管理

JNIEnv*也不能被缓存,因为它仅有效


同时,jmethodID和jfieldID可以跨JNI调用安全地重用——它们明确地标识JVM中的方法/字段,并且只要holder类还活着就可以使用。但是,如果holder类恰好被垃圾收集,它们也可能会变得无效。

谢谢。问题是在哪里可以找到本地或全局引用的已创建对象的文档。我重新阅读了这些文件,但并不是很清楚。除了jmethodID和jfieldID之外,还有其他对象可以在JNI调用中安全地重用吗?@St.Antario我已经用JNI规范的相关链接更新了答案。@apangin我听说保存太多也有问题,你可以很容易地在本地和全局引用中找到太多。请您解释一下哪些对象是安全的,不占用空间,哪些应该小心缓存?jclass、jobject、jfieldID、jmethodID。如果缓存本地或全局引用,这些限制是否有差异?@MarcinK。这是一个不同的主题,因此请提出一个单独的问题,以避免在评论中进行长时间的讨论。另外,请澄清你的确切意思。@apangin这里是我提出的一个问题:谢谢。问题是在哪里可以找到本地或全局引用的已创建对象的文档。我重新阅读了这些文件,但并不是很清楚。除了jmethodID和jfieldID之外,还有其他对象可以在JNI调用中安全地重用吗?@St.Antario我已经用JNI规范的相关链接更新了答案。@apangin我听说保存太多也有问题,你可以很容易地在本地和全局引用中找到太多。请您解释一下哪些对象是安全的,不占用空间,哪些应该小心缓存?jclass、jobject、jfieldID、jmethodID。如果缓存本地或全局引用,这些限制是否有差异?@MarcinK。这是一个不同的主题,因此请提出一个单独的问题,以避免在评论中进行长时间的讨论。另外,请澄清你的确切意思。@apangin这里是我提出的一个问题:
mov    rdi,r13
#...
mov    rdi,QWORD PTR [rdi]