type
status
date
slug
summary
tags
category
icon
password
启动JVM的通用流程
- 加载虚拟机实现的动态链接库(Android中是so文件);
一般通过
dlopen
方法加载so文件.- 设置启动参数,并使用
JNI_CreateJavaVM(JavaVM**, JNIEnv**, void*)
方法创建虚拟机;
这个时候就可以执行Java程序了.
我这里用C++写了个简单的例子,用来创建JVM:
C++创建虚拟机
#include <iostream> #include <jni.h> #include <dlfcn.h> int main() { auto jvmHandler = dlopen( "../libjvm.dylib", RTLD_NOW | RTLD_NODELETE); if (jvmHandler == nullptr) { std::cerr << "load libjvm.dylib failed" << std::endl; } JavaVM *javaVm; JNIEnv *jniEnv; JavaVMInitArgs vmInitArgs; JavaVMOption *options = new JavaVMOption[2]; options[0].optionString = "-Djava.class.path=."; options[1].optionString = "-version"; vmInitArgs.version = JNI_VERSION_1_4; vmInitArgs.nOptions = 1; vmInitArgs.options = options; vmInitArgs.ignoreUnrecognized = JNI_TRUE; auto status = JNI_CreateJavaVM(&javaVm, (void **) &jniEnv, &vmInitArgs); if (status < 0 || javaVm == nullptr || jniEnv == nullptr) { std::cerr << "Create JavaVM failed!" << std::endl; return 0; } delete (options); jclass cls = jniEnv->FindClass("Main"); if (jniEnv->ExceptionCheck() == JNI_TRUE || cls == nullptr) { jniEnv->ExceptionDescribe(); jniEnv->ExceptionClear(); free(jvmHandler); std::cerr << "find class Main failed!" << std::endl; return 0; } jmethodID mehtodTestId = jniEnv->GetStaticMethodID(cls, "main", "([Ljava/lang/String;)V"); jniEnv->CallStaticVoidMethod(cls, mehtodTestId, nullptr); javaVm->DestroyJavaVM(); return 0; }
代码没什么好说的,按照指定的流程加载就行,实际运用的时候启动参数得设置很多.
在使用app_process创建并启动zygote进程之前,会先启动虚拟机.
在AndroidRuntime::start()方法中有这么一段代码:
/* start the virtual machine */ JniInvocation jni_invocation; jni_invocation.Init(NULL); JNIEnv* env; if (startVm(&mJavaVM, &env, zygote, primary_zygote) != 0) { return; } onVmCreated(env);
JniInvocation
其实是一个中间层,通过这个中国层,那些使用JNI invocation API的应用可以动态选择JNI的实现方式.在Android中就是动态选择Dalvik虚拟机和ART虚拟机(可通过persist.sys.dalvik.vm.lib.2指定)bool Init(const char* library) { return JniInvocationInit(impl_, library) != 0; }
在
libnativehelper/JniInvocation.c
中:// 默认库 static const char* kDefaultJniInvocationLibrary = "libart.so"; bool JniInvocationInit(struct JniInvocationImpl* instance, const char* library_name) { #ifdef __ANDROID__ char buffer[PROP_VALUE_MAX]; #else char* buffer = NULL; #endif // 获取虚拟机库名,这里是libart.so library_name = JniInvocationGetLibrary(library_name, buffer); // 使用dlopen加载虚拟机库 DlLibrary library = DlOpenLibrary(library_name); if (library == NULL) { if (strcmp(library_name, kDefaultJniInvocationLibrary) == 0) { // Nothing else to try. ALOGE("Failed to dlopen %s: %s", library_name, DlGetError()); return false; } // Note that this is enough to get something like the zygote // running, we can't property_set here to fix this for the future // because we are root and not the system user. See // RuntimeInit.commonInit for where we fix up the property to // avoid future fallbacks. <http://b/11463182> ALOGW("Falling back from %s to %s after dlopen error: %s", library_name, kDefaultJniInvocationLibrary, DlGetError()); library_name = kDefaultJniInvocationLibrary; library = DlOpenLibrary(library_name); if (library == NULL) { ALOGE("Failed to dlopen %s: %s", library, DlGetError()); return false; } } // 查找对应JNI接口 DlSymbol JNI_GetDefaultJavaVMInitArgs_ = FindSymbol(library, "JNI_GetDefaultJavaVMInitArgs"); if (JNI_GetDefaultJavaVMInitArgs_ == NULL) { return false; } DlSymbol JNI_CreateJavaVM_ = FindSymbol(library, "JNI_CreateJavaVM"); if (JNI_CreateJavaVM_ == NULL) { return false; } DlSymbol JNI_GetCreatedJavaVMs_ = FindSymbol(library, "JNI_GetCreatedJavaVMs"); if (JNI_GetCreatedJavaVMs_ == NULL) { return false; } instance->jni_provider_library_name = library_name; instance->jni_provider_library = library; instance->JNI_GetDefaultJavaVMInitArgs = (jint (*)(void *)) JNI_GetDefaultJavaVMInitArgs_; instance->JNI_CreateJavaVM = (jint (*)(JavaVM**, JNIEnv**, void*)) JNI_CreateJavaVM_; instance->JNI_GetCreatedJavaVMs = (jint (*)(JavaVM**, jsize, jsize*)) JNI_GetCreatedJavaVMs_; return true; }
这里默认就是libart.so,然后使用dlopen系统调用进行加载,查找主要的JNI接口.然后将接口的实现切换为ART虚拟机.
在startVM中,首先会初始化大量的启动参数,然后就调用JNI_CreateJavaVM()方法创建ART虚拟机:
initArgs.version = JNI_VERSION_1_4; initArgs.options = mOptions.editArray(); initArgs.nOptions = mOptions.size(); initArgs.ignoreUnrecognized = JNI_FALSE; /* * Initialize the VM. * * The JavaVM* is essentially per-process, and the JNIEnv* is per-thread. * If this call succeeds, the VM is ready, and we can start issuing * JNI calls. */ if (JNI_CreateJavaVM(pJavaVM, pEnv, &initArgs) < 0) { ALOGE("JNI_CreateJavaVM failed\\n"); return -1; }
这个时候ART虚拟机就创建成功了.
- 作者:姜康
- 链接:https://jiangkang.tech/article/e7731213-4fca-4ff3-b902-3bd7ed1d528c
- 声明:本文采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处。
相关文章