我是靠谱客的博主 典雅雨,这篇文章主要介绍OpenJDK源码赏析之二:java虚拟机启动流程到首函数调用全流程,现在分享给大家,希望可以做个参考。

 承接上一谈 

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
54
int 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源码赏析之二内容请搜索靠谱客的其他文章。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(63)

评论列表共有 0 条评论

立即
投稿
返回
顶部