JNI_CreateJavaVM 上的段错误

Posted

技术标签:

【中文标题】JNI_CreateJavaVM 上的段错误【英文标题】:Segfault on JNI_CreateJavaVM 【发布时间】:2016-06-01 15:30:32 【问题描述】:

我第一次尝试通过 JNI 从 C++ 加载 JVM,但我似乎无法让它工作。调用 JNI_CreateJavaVM 时出现段错误。

代码非常简单(主要从在线示例复制):

#include<stdio.h>
#include<jni.h>

using namespace std;

int main(int argc, char** argv) 

    printf("Initializing JVM\n");
    JavaVM *jvm;
    JNIEnv *env;

    printf("Setting up args\n";
    JavaVMInitArgs vm_args;
    JavaVMOption* options = new JavaVMOption[1];
    options[0].optionString = "-Djava.class.path=.";
    vm_args.version = JNI_VERSION_1_6;
    vm_args.nOptions = 1;
    vm_args.options = options;
    vm_args.ignoreUnrecognized = false;

    printf("Attempting to create JVM\n");
    jint rc = JNI_CreateJavaVM(&jvm, (void**)&env, &vm_args);

    if(rc != JNI_OK) 
        printf("didn't work :(\n");
    
    else 
        printf("JVM load succeeded!\n");
        jint ver = env->GetVersion();
        printf("Version: %i.%i\n", (ver>>16)&0x0f, ver&0x0f);

        printf("Cleaning up\n");
        delete options;
        jvm->DestroyJavaVM();
    
    printf("Done\n");

我的 LD_LIBRARY_PATH 包含 /usr/java/jdk1.6.0_45/jre/lib/amd64/server 这是包含 libjvm.so 库的路径。我的系统上还有一些其他的 libjvm.so 库,但其中大部分是用于 java 1.4 的。只有一个其他 jdk 1.6 libjvm.so,我也尝试使用它,结果相同。

我编译:

g++ -g -c src/jniExpCppPart.cpp -I/usr/java/jdk1.6.0_45/include -I/usr/java/jdk1.6.0_45/include/linux -o obj/jniExpCppPart.o
g++ obj/jniExpCppPart.o -L/usr/java/jdk1.6.0_45/jre/lib/amd64/server -ljvm -o exe/jniExp

当我在 gdb 中运行它时,我得到了前三个打印语句,然后是:

Program received signal SIGSEGV, Segmentation fault
0x0000003249479e27 in strncmp () from /lib64/libc.so.6
(gdb) bt
#0 0x0000003249479e27 in strncmp () from /lib64/libc.so.6
#1 0x00002aaaaacd8c10 in Arguments::process_sun_java_launcher_properties(JavaVMInitArgs*) () from /usr/java/jdk1.6.0_45/jre/lib/amd64/server/libjvm.so
#2 0x00002aaaab2cfe7d in Thread::create_vm(JavaVMInitArgs*, bool*) () from /usr/java/jdk1.6.0_45/jre/lib/amd64/server/libjvm.so
#3 0x00002aaaaafcc800 in JNI_CreateJavaVM () from /usr/java/jdk1.6.0_45/jre/lib/amd64/server/libjvm.so
#4 0x0000000000400761 in main(argc=1, argv=0x7fffffffe568) at src/jni/ExpCppPart.cpp:22

我的猜测是,这个问题更多地与我的环境设置方式或我如何构建可执行文件而不是代码有关。自从我真正处理链接共享库以来已经有几年了,所以我肯定有可能搞砸了。

任何想法我可能做错了什么?

更新 我尝试使用 dlopen 加载库(因为我在一些使用 JNI 的 Linux 代码中看到了它)。它没有任何区别,但我想我会把它包括在这里,看看它是否能让任何人暗示我可能做错了什么。

我再次从未连接到互联网的系统中手动复制此内容,因此可能存在一些拼写错误。

#include<stdio.h>
#include<jni.h>
#include<dlfcn.h>

using namespace std;

//Create type for pointer to the JNI_CreateJavaVM function
typedef jint (*CreateJvmFuncPtr) (JavaVM**, void**, JavaVMInitArgs*);

//New method returns pointer to the JNI_CreateJavaVM function
CreateJvmFuncPtr findCreateJvm() 
    CreateJavaFuncPtr createJvm = NULL;

    void* jvmLib = dlopen("libjvm.so", RTLD_LAZY); //Get handle to jvm shared library
    char* error = dlerror(); //Check for errors on dlopen

    if(jvmLib = NULL || error != NULL) 
        printf("FailedToLoadJVM\n");
    

    //Load pointer to the function within the shared library
    createJvm = (CreateJvmFuncPtr) dlsym(jvmLib, "JNI_CreateJavaVM");

    error = dlerror();
    if(error != NULL) 
        printf("Success\n");
    

    return createJVM;


int main(int argc, char** argv) 

    printf("Initializing JVM\n");
    JavaVM *jvm;
    JNIEnv *env;

    printf("Setting up args\n";
    JavaVMInitArgs vm_args;
    JavaVMOption* options = new JavaVMOption[1];
    options[0].optionString = "-Djava.class.path=.";
    vm_args.version = JNI_VERSION_1_6;
    vm_args.nOptions = 1;
    vm_args.options = options;
    vm_args.ignoreUnrecognized = false;

    printf("Attempting to create JVM\n");
    //Old code: jint rc = JNI_CreateJavaVM(&jvm, (void**)&env, &vm_args);
    //New code:
    CreateJvmFuncPtr createJVM = findCreateJvm();
    printf("findCreateJVM() returned 0x%x\n", createJVM);

    jint rc = createJVM(&jvm, (void**)&env, &vm_args);
    //End new code

    if(rc != JNI_OK) 
        printf("didn't work :(\n");
    
    else 
        printf("JVM load succeeded!\n");
        jint ver = env->GetVersion();
        printf("Version: %i.%i\n", (ver>>16)&0x0f, ver&0x0f);

        printf("Cleaning up\n");
        delete options;
        jvm->DestroyJavaVM();
    
    printf("Done\n");

输出(与之前类似)显示一切都是成功的,直到它从 JNI_CreateJavaVM 方法中抛出一个段错误。

Initializing JVM
Setting up args
Attempting to create JVM
Success!
findCreateJVM() returned 0xc1e70780
Segmentation fault

所以看起来至少程序可以很好地找到库/函数。但是当它被调用时出现了问题。对我来说,这表明它可能只是一个简单的 API 误解(我传递的不是我应该传递的东西)或者共享库有一些奇怪的地方。如果共享库是针对与我的程序编译时不同的体系结构/字大小编译的,这可能会导致这种情况吗?如果是这样,我如何检查我的程序和库的目标架构?

【参考方案1】:

这很尴尬……

我终于发现了我的问题,堆栈溢出的任何人都无法修复它,因为我在问题中发布的代码实际上是正确的,但它与我运行的代码不同。

正如我所提到的,我在没有互联网访问的独立系统上运行此代码,因此我不得不手动复制代码。在复制它时,我纠正了我的错误。

我的独立系统上的代码包括以下几行:

JavaVMOption* options = new JavaVMOption[1];
options[0].optionString = "-Djava.class.path=.";
vm_args.version = JNI_VERSION_1_6;
vm_args.nOptions = 1;
vm_args.ignoreUnrecognized = false;

您会注意到与我在问题中发布的代码不同。因为漏掉了一行:

vm_args.options = options;

所以当然会导致段错误,因为 vm_args.options 从未设置为任何值。

【讨论】:

【参考方案2】:

你得到的错误在这一行:

jint rc = JNI_CreateJavaVM(&jvm, (void**)&&env, &vm_args);

您要的是指向 env 的指针地址的地址;而如果你看一下参考example source:

#include <jni.h>       /* where everything is defined */
...
JavaVM *jvm;       /* denotes a Java VM */
JNIEnv *env;       //<!-- same as yours
JavaVMInitArgs vm_args; /* JDK/JRE 6 VM initialization arguments */
JavaVMOption* options = new JavaVMOption[1];
options[0].optionString = "-Djava.class.path=/usr/lib/java";
vm_args.version = JNI_VERSION_1_6;
vm_args.nOptions = 1;
vm_args.options = options;
vm_args.ignoreUnrecognized = false;
/* load and initialize a Java VM, return a JNI interface
 * pointer in env */
JNI_CreateJavaVM(&jvm, (void**)&env, &vm_args); // <!-- NOT the same!
delete options;
/* invoke the Main.test method using the JNI */
jclass cls = env->FindClass("Main");
jmethodID mid = env->GetStaticMethodID(cls, "test", "(I)V");
env->CallStaticVoidMethod(cls, mid, 100);
/* We are done. */
jvm->DestroyJavaVM();

您应该将代码更改为:

jint rc = JNI_CreateJavaVM(&jvm, (void**)&env, &vm_args);

【讨论】:

糟糕,这实际上是一个错字。我正在开发的机器无法访问互联网,所以我不得不手动复制代码,结果搞砸了。在我实际尝试运行它的机器上,它只有一个'&'。我会在我的帖子中修复它。根据我发布的内容,您是绝对正确的。 如果它只是一个错字,那么代码应该可以工作。它在我尝试过的 mac 或 linux 上都没有失败 - 不过两者都运行最新的 java 8。 嗯...好吧,我的猜测是,如果它适用于 java 8,那么它将适用于 java 6(因为我复制的示例是为 java 6 制作的)。我认为这一定与我的环境设置方式有关。非常感谢您查看它。 我刚刚在 java 6 上测试了它——看起来和你的系统上的一样,而且它工作正常。我唯一的怀疑是options[0].optionString = "-Djava.class.path=."; 这行实际上并不是你在.cpp 文件中的内容。有一个strlen() 会捕获一个空指针,但如果你使用单引号,并故意转换为 char *,并忽略警告,我会触发与你相同的错误。 好主意,但不幸的是我使用了双引号,所以不是这样。

以上是关于JNI_CreateJavaVM 上的段错误的主要内容,如果未能解决你的问题,请参考以下文章

相当于 Windows/MSVC 上的段错误?

glEnableVertexArrayAttrib 生成的段错误

调试难以捉摸的段错误

glGenFramebuffers 中的段错误

调用 glDrawElements 时的段错误

Windows:处理所有线程中的段错误