我第一次尝试通过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误解(我传递的东西不是我应该的东西)或者共享库的奇怪之处。如果共享库是针对不同于我的程序编译的体系结构/字大小编译的,可能会导致这种情况吗?如果是这样,我如何检查我的程序和库的目标架构?
答案 0 :(得分:0)
你得到的错误就在这一行:
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);
答案 1 :(得分:0)
这是令人尴尬的......
我终于发现了我的问题,并且堆栈溢出的任何人都无法修复它,因为我在问题中发布的代码实际上是正确的,但它与我正在运行的代码不同。
正如我所提到的,我在没有Internet访问权限的独立系统上运行此代码,因此我不得不手动复制代码。在复制它时,我纠正了我的错误。
我的独立系统上的代码包括以下几行:
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从未设置为任何内容。