在启动JVM之前确定JNI/JVM版本



我有一个使用JNI嵌入JVM并支持各种Java版本的C应用程序。该应用程序在linux, OSX和windows上运行。

目前它允许通过环境变量提供JVM参数,但我想让它更智能。

不幸的是,我需要根据Java版本设置一些不同的VM参数。

如果我有一个使用GetVersionJNIEnv*,我可以得到这个版本——但是当我有一个JNIEnv*的时候,我已经启动了一个JVM,已经太晚了。(你不能关闭并重新启动一个新的JVM)

它看起来像JNI_GetDefaultJavaVMInitArgs会做的伎俩-但是,虽然它编译OK我不能得到一个明智的答案。例如,使用Java 9:

JavaVMInitArgs vmArgs;
vmArgs.version = JNI_VERSION_1_2;
if (JNI_GetDefaultJavaVMInitArgs(&vmArgs)!=JNI_OK) {
fprintf(stderr, "JNI_GetDefaultJavaVMInitArgs failed!n");
};
fprintf(stderr, "Query with version = %xn", JNI_VERSION_1_2);
fprintf(stderr, "JAVA version = %xn", vmArgs.version);

打印

Query with version = 10002
JAVA version = 10002

。没有更新版本。

是否有方法(以跨平台的方式)获得此信息?我在使用JNI_GetDefaultJavaVMInitArgs时做错了什么吗?

为什么不先做最简单的事情,在单独的进程中启动JVM来找出JVM的版本:

int fds[2];
pipe(fds);
if (fork() == 0) { // child
close(fds[0]);
JavaVMInitArgs vm_args;
vm_args.version = JNI_VERSION_1_2;
vm_args.options = nullptr;
vm_args.nOptions = 0;
vm_args.ignoreUnrecognized = true;
JNIEnv *env = nullptr;
jint res = JNI_CreateJavaVM(&vm, (void **)&env, &vm_args);
jclass cls_Runtime = env->FindClass("java/lang/Runtime");
jmethodID mid_version = env->GetStaticMethodID("version", "()Ljava/lang/Runtime$Version;");
jobject obj_Version = env->CallStaticObjectMethod(cls_Runtime, mid_getVersion);
jclass cls_Version = env->GetObjectClass(obj_Version);
jmethodID mid_toString = env->getMethodID(cls_Version, "toString", "()Ljava/lang/String;");
jstring str_Version = (jstring) env->CallObjectMethod(obj_version, mid_toString);
const char *version = env->GetStringUTFChars(str_Version, null);
write(fds[1], version, env->GetStringUTFLength(str_Version));
exit(0);
} else if (fork() > 0) { // parent
close(fds[1]);
char version[100];
int size = read(fds[0], version, 100);
close(fds[0]);
version[size] = ''; 
// continue initialization
} else { // error }

不调用toString,你当然可以直接调用feature()

您必须区分JNI和JVM。每个JVM版本根据其版本支持一组JNI API(参见https://docs.oracle.com/en/java/javase/18/docs/specs/jni/functions.html#version-information获取最新列表)。当您请求激活JVM时,您必须设置编写应用程序的JNI的最低版本;C语言的链接是增量的,例如,GetModule从版本9开始可用。如果调用CreateJavaVM时使用的JNI版本高于JVM支持的JNI版本,API将返回jni_version。

如果应用程序的参数依赖于JVM版本,则JNI版本是无用的:最新的JNI版本是10,从JRE 10到JRE 18都是一样的。

为了能够调用JNI API,您必须加载JVM主库。当您加载JVM库时,您将决定使用哪个版本的JVM,从而决定JNI版本。一个系统可以安装多个jvm,一个是默认的,而另一个可以使用jvm.dll, libjvm的路径访问。所以,等等。通常,JVM的版本是在路径中编写的,它取决于许多东西,比如规范、构建、供应商和平台。这是apt的输出:

sudo apt install openjdk-11-jre-headless  # version 11.0.10+9-0ubuntu1~20.04, or
sudo apt install default-jre              # version 2:1.11-72
sudo apt install openjdk-8-jre-headless   # version 8u282-b08-0ubuntu1~20.04
sudo apt install openjdk-13-jre-headless  # version 13.0.4+8-1~20.04
sudo apt install openjdk-14-jre-headless  # version 14.0.2+12-1~20.04

您可以解析JVM库的路径,尝试识别JRE规范(8,11,13,…),然后您可以使用应用程序在该版本中需要的参数调用CreateJavaVM。

最新更新