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

概述

 承接上一谈 

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

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

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

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

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

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

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

 初始化虚拟机

/*
 * 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源码

/*
 * 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源码赏析之二:java虚拟机启动流程到首函数调用全流程所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

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

评论列表共有 0 条评论

立即
投稿
返回
顶部