承接上一谈
OpenJDK源码赏析之一:漫谈java的历史渊源_星空_AZ的博客-CSDN博客
JAVA从启动到第一个函数执行的发生的流程:
WinMain->JLI_Launch->JVMInit->NewThread->JavaMain->initializateJVM->CreatJavaVM->LoadMainClass->GetStaticMethodID
看一个程序首先要找它的启动入口
OpenJDK的如下目录有着main程序的启动入口:
打开后是程序的入口,main的函数如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54int WINAPI WinMain(HINSTANCE inst, HINSTANCE previnst, LPSTR cmdline, int cmdshow) { int margc; char** margv; const jboolean const_javaw = JNI_TRUE; __initenv = _environ; #else /* JAVAW */ int main(int argc, char **argv) { int margc; char** margv; const jboolean const_javaw = JNI_FALSE; #endif /* JAVAW */ #ifdef _WIN32 { int i = 0; if (getenv(JLDEBUG_ENV_ENTRY) != NULL) { printf("Windows original main args:n"); for (i = 0 ; i < __argc ; i++) { printf("wwwd_args[%d] = %sn", i, __argv[i]); } } } JLI_CmdToArgs(GetCommandLine()); margc = JLI_GetStdArgc(); // add one more to mark the end margv = (char **)JLI_MemAlloc((margc + 1) * (sizeof(char *))); { int i = 0; StdArg *stdargs = JLI_GetStdArgs(); for (i = 0 ; i < margc ; i++) { margv[i] = stdargs[i].arg; } margv[i] = NULL; } #else /* *NIXES */ margc = argc; margv = argv; #endif /* WIN32 */ return JLI_Launch(margc, margv, sizeof(const_jargs) / sizeof(char *), const_jargs, sizeof(const_appclasspath) / sizeof(char *), const_appclasspath, FULL_VERSION, DOT_VERSION, (const_progname != NULL) ? const_progname : *margv, (const_launcher != NULL) ? const_launcher : *margv, (const_jargs != NULL) ? JNI_TRUE : JNI_FALSE, const_cpwildcard, const_javaw, const_ergo_class); }
可以看到最后运行一个JLI_Launch的函数,JLI的全称为Java Native Interface,是java调用非java代码的接口,这个函数在java.c中, 找到java.c中该函数的位置
看到 CreateExecutionEnvironment,为创建执行环境
跳转到该函数实现位置
其实现了以下几个功能
①判断环境为32位或者64位
②得到JRE的路径
③ReadKnownVms()得到JVM.cfg文件(该文件为java的配置文件)
④CheckJvmType确认当前的jvm类型
⑤GetJVMPath根据上一步确定的JVM类型,找到对应的JVM.dll文件
继续回到java.c中的JLI_Launch函数,下面执行LoadJavaVM
跳转到该函数其实现了加载dll库,并绑定函数可以调用dll中的
LoadLibrary链接到DLL库(LoadLibrary为加载动态链接库),此库为jvm.dll
把JVM.dll文件中定义的函数JNI_CreateJavaVM和JNI_GetDefaultJavaVMInitArgs的指针绑定到InvocationFunctions变量的CreateJavaVM和GetDefaultJavaVMInitArgs函数指针变量上;
当装载好虚拟机所需要的环境之后,回到JLI_Launch函数最后一步要执行的JVMInit
跳转到JVMInit后,开始通过ContinueInNewThread创建新的Java进程
在ContinueInNewThread下有个函数ContinueInNewThread0,执行了当前的主方法JavaMain
在JavaMain中有一个InitializeJVM
初始化虚拟机
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32/* * Initializes the Java Virtual Machine. Also frees options array when * finished. */ static jboolean InitializeJVM(JavaVM **pvm, JNIEnv **penv, InvocationFunctions *ifn) { JavaVMInitArgs args; jint r; memset(&args, 0, sizeof(args)); args.version = JNI_VERSION_1_2; args.nOptions = numOptions; args.options = options; args.ignoreUnrecognized = JNI_FALSE; if (JLI_IsTraceLauncher()) { int i = 0; printf("JavaVM args:n "); printf("version 0x%08lx, ", (long)args.version); printf("ignoreUnrecognized is %s, ", args.ignoreUnrecognized ? "JNI_TRUE" : "JNI_FALSE"); printf("nOptions is %ldn", (long)args.nOptions); for (i = 0; i < numOptions; i++) printf(" option[%2d] = '%s'n", i, args.options[i].optionString); } r = ifn->CreateJavaVM(pvm, (void **)penv, &args); JLI_MemFree(options); return r == JNI_OK; }
最后有CreatJavaVM,初始化虚拟机
最后创建Java虚拟机进程后,回到JavaMain函数下面有这么一行:
把mainClass赋值为我们写的调用的主函数,以下为LoadMainClass源码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35/* * Loads a class and verifies that the main class is present and it is ok to * call it for more details refer to the java implementation. * 加载一个类并验证主类是否存在,可以调用它以获取更多详细信息,请参阅java实现。 */ static jclass LoadMainClass(JNIEnv *env, int mode, char *name) { jmethodID mid; jstring str; jobject result; jlong start, end; jclass cls = GetLauncherHelperClass(env); NULL_CHECK0(cls); if (JLI_IsTraceLauncher()) { start = CounterGet(); } NULL_CHECK0(mid = (*env)->GetStaticMethodID(env, cls, "checkAndLoadMain", "(ZILjava/lang/String;)Ljava/lang/Class;")); str = NewPlatformString(env, name); result = (*env)->CallStaticObjectMethod(env, cls, mid, USE_STDERR, mode, str); if (JLI_IsTraceLauncher()) { end = CounterGet(); printf("%ld micro seconds to load main classn", (long)(jint)Counter2Micros(end-start)); printf("----%s----n", JLDEBUG_ENV_ENTRY); } return (jclass)result; }
mainClass赋值完成以后,返回JavaMain函数接着执行下面的操作:
用CallStaticVoidMethod调用我们在Java里面写的Java入口函数mainClass
至此从java启动到运行第一个类的全逻辑已经理了一遍
想知道在命令行中输入java -help的流程吗,我将在下一篇将分析cmd下java命令行的读取
OpenJDK源码赏析之三:cmd下Java命令参数的读取_星空_AZ的博客-CSDN博客
最后
以上就是典雅雨最近收集整理的关于OpenJDK源码赏析之二:java虚拟机启动流程到首函数调用全流程的全部内容,更多相关OpenJDK源码赏析之二内容请搜索靠谱客的其他文章。
发表评论 取消回复