前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Java的main方法是如何被调用的

Java的main方法是如何被调用的

作者头像
KINGYT
发布2023-03-15 13:43:42
1.3K0
发布2023-03-15 13:43:42
举报

本文将从源码角度看下Java的main方法是如何被调用的。OpenJDK版本

➜ jdk hg id 76072a077ee1+ jdk-11+28

当我们运行Java命令后,Java程序本身的main方法会首先被执行

C文件src/java.base/share/native/launcher/main.c

代码语言:javascript
复制
JNIEXPORT int
main(int argc, char **argv)
{
    int margc;
    char** margv;
    int jargc;
    char** jargv;
    const jboolean const_javaw = JNI_FALSE;
    ...
    return JLI_Launch(margc, margv,
                   jargc, (const char**) jargv,
                   0, NULL,
                   VERSION_STRING,
                   DOT_VERSION,
                   (const_progname != NULL) ? const_progname : *margv,
                   (const_launcher != NULL) ? const_launcher : *margv,
                   jargc > 0,
                   const_cpwildcard, const_javaw, 0);
}

之后进入JLI_Launch方法

C文件src/java.base/share/native/libjli/java.c

代码语言:javascript
复制
/*
 * Entry point.
 */
JNIEXPORT int JNICALL
JLI_Launch(int argc, char ** argv,              /* main argc, argv */
        int jargc, const char** jargv,          /* java args */
        int appclassc, const char** appclassv,  /* app classpath */
        const char* fullversion,                /* full version defined */
        const char* dotversion,                 /* UNUSED dot version defined */
        const char* pname,                      /* program name */
        const char* lname,                      /* launcher name */
        jboolean javaargs,                      /* JAVA_ARGS */
        jboolean cpwildcard,                    /* classpath wildcard*/
        jboolean javaw,                         /* windows-only javaw */
        jint ergo                               /* unused */
)
{
    ...
    InvocationFunctions ifn;
    ...
    char jvmpath[MAXPATHLEN];
    char jrepath[MAXPATHLEN];
    char jvmcfg[MAXPATHLEN];
    ...


    // 找到jvmpath,例如 /usr/lib/jvm/jdk-11/lib/server/libjvm.so
    CreateExecutionEnvironment(&argc, &argv,
                               jrepath, sizeof(jrepath),
                               jvmpath, sizeof(jvmpath),
                               jvmcfg,  sizeof(jvmcfg));


    ...
    // 加载libjvm.so,并获取其 JNI_CreateJavaVM 方法
    if (!LoadJavaVM(jvmpath, &ifn)) {
        return(6);
    }
    ...
    return JVMInit(&ifn, threadStackSize, argc, argv, mode, what, ret);
}

之后进入JVMInit方法

C文件src/java.base/unix/native/libjli/java_md_solinux.c

代码语言:javascript
复制
int
JVMInit(InvocationFunctions* ifn, jlong threadStackSize,
        int argc, char **argv,
        int mode, char *what, int ret)
{
    ShowSplashScreen();
    return ContinueInNewThread(ifn, threadStackSize, argc, argv, mode, what, ret);
}

再进入ContinueInNewThread方法

C文件src/java.base/share/native/libjli/java.c

代码语言:javascript
复制
int
ContinueInNewThread(InvocationFunctions* ifn, jlong threadStackSize,
                    int argc, char **argv,
                    int mode, char *what, int ret)
{
    ...
    { /* Create a new thread to create JVM and invoke main method */
      JavaMainArgs args;
      int rslt;


      args.argc = argc;
      args.argv = argv;
      args.mode = mode;
      args.what = what;
      args.ifn = *ifn;


      rslt = ContinueInNewThread0(JavaMain, threadStackSize, (void*)&args);
      /* If the caller has deemed there is an error we
       * simply return that, otherwise we return the value of
       * the callee
       */
      return (ret != 0) ? ret : rslt;
    }
}

该方法最终会调用ContinueInNewThread0方法,开启一个系统线程,且该线程的入口函数是JavaMain。看下JavaMain方法

C文件src/java.base/share/native/libjli/java.c

代码语言:javascript
复制
int JNICALL
JavaMain(void * _args)
{
    ...
    // 该方法会调用libjvm.so里的JNI_CreateJavaVM方法对JVM进行初始化
    if (!InitializeJVM(&vm, &env, &ifn)) {
        JLI_ReportErrorMessage(JVM_ERROR1);
        exit(1);
    }
    ...
    // 如果我们运行Java时传入了--version参数,下面就会输出version并结束Java进程
    if (printVersion || showVersion) {
        PrintJavaVersion(env, showVersion);
        CHECK_EXCEPTION_LEAVE(0);
        if (printVersion) {
            LEAVE();
        }
    }


    ...
    mainClass = LoadMainClass(env, mode, what);
    ...
    mainArgs = CreateApplicationArgs(env, argv, argc);
    ...
    mainID = (*env)->GetStaticMethodID(env, mainClass, "main",
                                       "([Ljava/lang/String;)V");
    ...
    /* Invoke main method. */
    (*env)->CallStaticVoidMethod(env, mainClass, mainID, mainArgs);


    ...
    ret = (*env)->ExceptionOccurred(env) == NULL ? 0 : 1;
    LEAVE();
}

该方法就是我们的最终方法,它会先调用InitializeJVM初始化JVM,再通过一系列的方法获取mainClass、mainArgs,最终调用(*env)->CallStaticVoidMethod 方法真正的执行我们提供的Java main方法。

至此,整个流程也就结束了。

有关(*env)->CallStaticVoidMethod究竟是如何执行的Java main方法,以及Java main方法又是如何调用的其他Java方法,我们之后会另起文章详细分析。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2019-01-11,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 卯时卯刻 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档