找到jvm.dll之后呢?——加载。
也就是获取到应该加载的jvmPath之后,java.c接下来执行到263行:
if (!LoadJavaVM(jvmpath, &ifn)) {
exit(6);
}
,通过java_md.c的LoadJavaVM函数来加载整个
JVM虚拟机,该方法有2个参数: 第一个是我们之前获取得到的jvm.dll的绝对路径;
另一个是main函数一开始就初始化的一个结构体InvocationFunctions。
LoadJVM首先通过LoadLibrary函数加载了jvm.dll,然后把其中的2个方法JNI_CreateJavaVM 和 JNI_Get
DefaultJavaVMInitArgs 挂载到参数ifn的结构体中。
/* Load the Java VM DLL */
if ((handle = LoadLibrary(jvmpath)) == 0) {
ReportErrorMessage2("Error loading: %s", (char *)jvmpath, JNI_TRUE);
return JNI_FALSE;
}
/* Now get the function addresses */
ifn->CreateJavaVM =
(void *)GetProcAddress(handle, "JNI_CreateJavaVM");
ifn->GetDefaultJavaVMInitArgs =
(void *)GetProcAddress(handle, "JNI_GetDefaultJavaVMInitArgs");
加载完jvm.dll,接下来main函数继续初始化接收到的所有参数,然后把所有初始化好的jvm参数和之前我们加载好的InvocationFunctions一起赋值给JavaMainArgs
struct JavaMainArgs {
int argc;
char ** argv;
char * jarfile;
char * classname;
InvocationFunctions ifn;
};
构造好JavaMainArgs 之后,会执行到java.c的370行。
ContinueInNewThread(JavaMain, threadStackSize, (void*)&args, ret);
新起一个
线程去执行JavaMain函数。
JavaMain函数经过一系列初始化之后,会调用java.c中的InitializeJVM函数来初始化一个JVM虚拟机,该函数主要执行逻辑就一句话,在java.c的1269行
r = ifn->CreateJavaVM(pvm, (void **)penv, &args);
。
就是通过调用我们之前挂在的InvocationFunctions的CreateJVM创建一个虚拟机,而CreateJVM挂载的是jvm.dll中的JNI_CreateJavaVM方法。JNI_CreateJavaVM方法的源代码在\
hotspot\src\share\vm\prims\jni.cpp中。该方法的定义如下:
_JNI_IMPORT_OR_EXPORT_ jint JNICALL JNI_CreateJavaVM(JavaVM **vm, void **penv, void *args)
该方法有3个参数,都是从java.c的InitializeJVM函数传过来的: 第一个参数是javaVM指针的指针;
第二个参数是JNIEnv类型的指针的指针;
第三个参数是一个虚拟机的参数的指针。该函数主要的功能
result = Threads::create_vm((JavaVMInitArgs*) args, &can_try_again);
if (result == JNI_OK) {
JavaThread *thread = JavaThrad::current();
/* thread is thread_in_vm heere */
*vm = (JavaVM *)(&main_vm);
*(JNIEnv**)penv = thread->jni_environment();
一是根据jvm启动参数构建一个VM虚拟机;
二是构建了一个JNIEnv的实例挂载到main中的JNIEnv结构的指针。这样,java.c的InitializeJVM函数就可以使用这个JNIEnv实例继续做下面的操作。
构造好了VM实例和JNIEnv,JavaMain函数开始加载Class,它首先会寻找java程序的入口类,即MainClass。
if (jarfile != 0) {
mainClassName = GetMainClassName(env, jarfile);
.....
} else {
mainClassName = NewPlatformString(env, classname);
....
}
如果执行的是jar文件,则会通过GetMainClassName函数查找jar中的MainClass.具体逻辑就是先实例化java/util/jar/JarFile类,然后获取执行的jar文件中的Manifest对象的"Main-Class"属性。——即jar包中的META-INF/MANIFEST.MF文件的"Main-Class"属性。
如果直接是执行的java类,就直接以该类为MainClass。然后调用GetStaticMethodID函数获MainClass中的public static void main(str[] args)方法,找到MainClass中的入口函数,就会初始化MainClass的参数
mainArgs = NewPlatformStringArray(env, argv, argc);
,然后调用前面获取到的JNIEnv实例调用CallStaticVoidMethod方法执行入口函数。
/* Invoke main method. */
(*env)->CallStaticVoidMethod(env, mainClass, mainID, mainArgs);
。
这样,程序才执行到我们自己所写的代码。