失眠网,内容丰富有趣,生活中的好帮手!
失眠网 > OpenJDK源码赏析之二:java虚拟机启动流程到首函数调用全流程

OpenJDK源码赏析之二:java虚拟机启动流程到首函数调用全流程

时间:2022-12-10 15:07:48

相关推荐

OpenJDK源码赏析之二:java虚拟机启动流程到首函数调用全流程

承接上一谈

OpenJDK源码赏析之一:漫谈java的历史渊源_星空_AZ的博客-CSDN博客

JAVA从启动到第一个函数执行的发生的流程:

WinMain->JLI_Launch->JVMInit->NewThread->JavaMain->initializateJVM->CreatJavaVM->LoadMainClass->GetStaticMethodID

看一个程序首先要找它的启动入口

OpenJDK的如下目录有着main程序的启动入口:

打开后是程序的入口,main的函数如下:

int WINAPIWinMain(HINSTANCE inst, HINSTANCE previnst, LPSTR cmdline, int cmdshow){int margc;char** margv;const jboolean const_javaw = JNI_TRUE;__initenv = _environ;#else /* JAVAW */intmain(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] = %s\n", i, __argv[i]);}}}JLI_CmdToArgs(GetCommandLine());margc = JLI_GetStdArgc();// add one more to mark the endmargv = (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

初始化虚拟机

/** Initializes the Java Virtual Machine. Also frees options array when* finished.*/static jbooleanInitializeJVM(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 %ld\n", (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源码

/** 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 jclassLoadMainClass(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 class\n",(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虚拟机启动流程到首函数调用全流程》对你有帮助,请点赞、收藏,并留下你的观点哦!

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。