【问题标题】:JNI "env->GetStaticMethodID()" crashed programJNI“env->GetStaticMethodID()”程序崩溃
【发布时间】:2011-09-26 17:46:57
【问题描述】:

我正在尝试从 C++ 调用 Java 函数。 到目前为止,这是我的代码:

#include <jni.h>

typedef struct JavaVMCreationResult {
    JavaVM* jvm;
    JNIEnv* env;
} JVMCreationResult;

JVMCreationResult* CreateJavaVM() {
    JavaVM* jvm;
    JNIEnv* env;

    JavaVMInitArgs args;

    JavaVMOption opts[1];
    opts[0].optionString = "-Djava.class.path=C:\\MyJavaClasses";

    args.version = JNI_VERSION_1_6;
    args.nOptions = 1;
    args.options = opts;
    args.ignoreUnrecognized = JNI_TRUE;

    JNI_GetDefaultJavaVMInitArgs(&args);

    JNI_CreateJavaVM(&jvm, (void **) &env, &args);

    JavaVMCreationResult* cres;
    cres->jvm = jvm;
    cres->env = env;

    return cres;
}

int main() {
    JVMCreationResult* cres = CreateJavaVM();
    JavaVM* jvm = cres->jvm;
    JNIEnv* env = cres->env;

    jclass cls = env->FindClass("Main");
    jmethodID mid = env->GetStaticMethodID(cls, "main", "([Ljava/lang/String;)V"); // the evil line
}

我在 Windows 7 上使用带有 MinGW GCC 的 Code::Blocks。
main() 函数的最后一行使程序崩溃,但编译器没有抱怨任何事情。 (注释掉 jmethodID mid = env-&gt;GetSta... 行会使程序“不崩溃”)
我使用javap -s Main 来获得正确的方法签名,该类也是一个有效的Java 类。

你能告诉我程序崩溃的原因吗?这个例子只是在互联网上随处可见,但它对我不起作用。 :(

这是 Java 类:

public class Main {
    public static void main(String[] args) {
        System.out.println("This is from Java !");
    }
}

解决方案

我没想到,当结构未初始化时,程序并没有早早崩溃,这对我来说似乎是不合逻辑的。但这确实是问题所在。
这是完整且有效的代码!

#include <jni.h>

#ifndef null
#define null NULL
#endif

typedef struct JavaVMCreationResult {
    JavaVM* jvm;
    JNIEnv* env;
} JVMCreationResult;

JVMCreationResult* CreateJavaVM() {
    JavaVM* jvm;
    JNIEnv* env;

    JavaVMInitArgs args;

    JavaVMOption opts[1];
    opts[0].optionString = "-Djava.class.path=C:\\Users\\Claudia\\Desktop";

    args.version = JNI_VERSION_1_6;
    args.nOptions = 1;
    args.options = opts;
    args.ignoreUnrecognized = JNI_TRUE;

    JNI_GetDefaultJavaVMInitArgs(&args);

    JNI_CreateJavaVM(&jvm, (void **) &env, &args);

    JVMCreationResult* cres = new JVMCreationResult();
    cres->jvm = jvm;
    cres->env = env;

    return cres;
}

int main() {
    JVMCreationResult* cres = CreateJavaVM();
    JavaVM* jvm = cres->jvm;
    JNIEnv* env = cres->env;

    jclass cls = env->FindClass("Main");
    if (cls) {
        printf("Yes !\n");
    }
    else {
        printf("No !\n");
    }
    jmethodID mid = env->GetStaticMethodID(cls, "main", "([Ljava/lang/String;)V");
    env->CallStaticVoidMethod(cls, mid);

    printf("At end of Program.");
}

【问题讨论】:

  • 您的变量“cres”是 CreateJavaVM 调用中从未初始化的点,因此您可能在该点取消引用空指针或其他无效指针。
  • 这不是意味着程序应该在JavaVM* jvm = cres-&gt;jvm; 处崩溃吗?为什么它没有初始化? (我不是 C++ 专家,几周前就开始了。)谢谢
  • 你怎么知道它在 main() 的最后一行崩溃了?
  • @Niklas 是的,cres 没有指向分配的内存。实际上,如果有什么问题,它应该会在 cres-&gt;jvm = jvm 上崩溃。
  • 编译器无法检测空指针,因为它们是在运行时生成的。

标签: java c++ java-native-interface


【解决方案1】:

您的变量“cres”是 CreateJavaVM 调用中从未初始化的点,因此您可能在该点取消引用空指针或其他无效指针。

一种解决方法是在main中定义cres(不是cres的指针),将that的指针作为参数传递给CreateJavaVM,然后使用CreateJavaVM内部的参数返回结果。

在 JNI_CreateJavaVM 调用之后检查 jvm 和 env 是否获得非空值也是一个好主意,并且在分别调用 FindClass 和 GetStaticMethodID 之后 cls 和 mid 同样是非空的

【讨论】:

  • 这真的是问题所在,这让我很困惑!为什么程序没有在jclass cls = env-&gt;FindClass("Main"); 崩溃?!初始化结构有效!非常感谢!
【解决方案2】:

cls 可能无效。我认为如果 'cres' 为空,您的程序会更早崩溃。

int main() {
    JVMCreationResult* cres = CreateJavaVM();
    if(!cres) return -1;
    JavaVM* jvm = cres->jvm;
    JNIEnv* env = cres->env;

    jclass cls = env->FindClass("Main");
    if(env->ExceptionCheck()) {    // ClassNotFoundException ?
        env->ExceptionDescribe();
        env->ExceptionClear();
    }
    if(!cls) return -2;    // this I think is your problem
    jmethodID mid = env->GetStaticMethodID(cls, "main", "([Ljava/lang/String;)V"); // the evil line
}

您确定您的类路径已正确指定吗?希望 FindClass("Main") 会找到一个默认的包类。无论如何,如果你的 C/C++ main() 现在告诉我们返回值。

“JavaVM* jvm = cres->jvm;”是可能的被优化掉,因为“jvm”从未被引用过,并且“cres->jvm”语句没有副作用。一些评论者说它应该会崩溃,嗯,是的,如果代码是生成然后执行的。但是一个体面的编译器可能会认为这是一个无操作。

但是语句“JNIEnv* env = cres->env;”无法优化掉,因为稍后会使用变量“env”。所以我们只能声称如果 cres==0 那么它会在执行时或之前崩溃。由于 "env" 用于 FindClass() 调用,因此我们确定 env!=0 并且因此 cres!=0。

我猜你有一个类路径设置问题,FindClass() 在运行时找不到你的类,这导致“cls==0”为真。这是我的答案。

已编辑:我看到其他人对“cres”的说法,但这并没有改变我最初的诊断,但您仍然有关于“cres”的错误,请将行更改为:

JavaVMCreationResult* cres = new JavaVMCreationResult;

我认为你很幸运 cres 指向某个地方(可能在堆栈上),然后你将值复制到本地 main() 堆栈并使用这些值。但这并不能使技术正确,因为“cres”指向的初始内存是随机的,所以你很幸运没有发生崩溃,但你确实在你不应该拥有的内存上乱涂乱画。通过使用“cres = new JavaVMCreationResult;”这会导致指针被设置为一个已知的有效内存块。

如果您希望编译器帮助解决此问题(即它应该显示警告),请在编译期间尝试使用 MinGW“-Wall”和“-O2”选项。它应该警告未初始化变量“cres”的使用。

【讨论】:

  • 感谢您的回答,这对我来说确实听起来更合乎逻辑,但是,这确实是结构未初始化的原因。对我来说很奇怪,还是谢谢! + 1
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-12-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-08-24
相关资源
最近更新 更多