本文将从源码角度看下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方法,我们之后会另起文章详细分析。