1.Launcher线程
1.1 native/launcher/main.c/main
启动JVM进程的启动器,一般情况是java。
Laucher只是一个封装了VM的执行外壳,由它负责装载JRE环境和libjvm.so。在一个JVM进程内容,只能执行一个指定的Java程序。
java对应的源码为:/jdk/src/java.base/share/native/launcher/main.c
int
main(int argc, char **argv)
{
int margc;
char** margv;
const jboolean const_javaw = JNI_FALSE;
#endif /* JAVAW */
JLI_InitArgProcessing(!HAS_JAVA_ARGS, const_disable_argfile);
#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 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 */
{
// accommodate the NULL at the end
JLI_List args = JLI_List_new(argc + 1);
int i = 0;
// Add first arg, which is the app name
JLI_List_add(args, JLI_StringDup(argv[0]));
// Append JDK_JAVA_OPTIONS
if (JLI_AddArgsFromEnvVar(args, JDK_JAVA_OPTIONS)) {
// JLI_SetTraceLauncher is not called yet
// Show _JAVA_OPTIONS content along with JDK_JAVA_OPTIONS to aid diagnosis
if (getenv(JLDEBUG_ENV_ENTRY)) {
char *tmp = getenv("_JAVA_OPTIONS");
if (NULL != tmp) {
JLI_ReportMessage(ARG_INFO_ENVVAR, "_JAVA_OPTIONS", tmp);
}
}
}
// Iterate the rest of command line
for (i = 1; i < argc; i++) {
JLI_List argsInFile = JLI_PreprocessArg(argv[i]);
if (NULL == argsInFile) {
JLI_List_add(args, JLI_StringDup(argv[i]));
} else {
int cnt, idx;
cnt = argsInFile->size;
for (idx = 0; idx < cnt; idx++) {
JLI_List_add(args, argsInFile->elements[idx]);
}
// Shallow free, we reuse the string to avoid copy
JLI_MemFree(argsInFile->elements);
JLI_MemFree(argsInFile);
}
}
margc = args->size;
// add the NULL pointer at argv[argc]
JLI_List_add(args, NULL);
margv = args->elements;
}
#endif /* WIN32 */
return JLI_Launch(margc, margv,
sizeof(const_jargs) / sizeof(char *), const_jargs,
0, NULL,
VERSION_STRING,
DOT_VERSION,
(const_progname != NULL) ? const_progname : *margv,
(const_launcher != NULL) ? const_launcher : *margv,
HAS_JAVA_ARGS,
const_cpwildcard, const_javaw, 0);
}
大体流程分为两大步:
- step1.处理参数
- step2.调用JLI_Lauch方法
1.2 native/libjli/java.c/JLI_Launch
JLI_Launch方法位于jdk/src/java.base/share/native/libjli/java.c
/*
* Entry point.
*/
int
JLI_Launch(int argc, char ** argv, /* main argc, argc */
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 */
)
{
int mode = LM_UNKNOWN;
char *what = NULL;
char *main_class = NULL;
int ret;
InvocationFunctions ifn;
jlong start, end;
char jvmpath[MAXPATHLEN];
char jrepath[MAXPATHLEN];
char jvmcfg[MAXPATHLEN];
_fVersion = fullversion;
_launcher_name = lname;
_program_name = pname;
_is_java_args = javaargs;
_wc_enabled = cpwildcard;
//根据环境变量初始化debug标志位,后续的日志是否会打印靠这个debug标志控制了
InitLauncher(javaw);
//如果设置了debug,就会打印一些辅助信息
DumpState();
//如果设置debug标志位,就打印命令行参数,并加入额外参数
if (JLI_IsTraceLauncher()) {
int i;
printf("Command line args:\n");
for (i = 0; i < argc ; i++) {
printf("argv[%d] = %s\n", i, argv[i]);
}
AddOption("-Dsun.java.launcher.diag=true", NULL);
}
/*
* SelectVersion() has several responsibilities:
*
* 1) Disallow specification of another JRE. With 1.9, another
* version of the JRE cannot be invoked.
* 2) Allow for a JRE version to invoke JDK 1.9 or later. Since
* all mJRE directives have been stripped from the request but
* the pre 1.9 JRE [ 1.6 thru 1.8 ], it is as if 1.9+ has been
* invoked from the command line.
*/
//选择jre版本,在jar包的manifest文件或者命令行中都可以对jre版本进行设置
SelectVersion(argc, argv, &main_class);
/*设置一些参数,例如jvmpath的值被设置成:
/home/wz/openjdk/openjdk-jdk9-jdk9/build/
linux-x86_64-normal-server-slowdebug/
jdk/lib/server/libjvm.so
*/
CreateExecutionEnvironment(&argc, &argv,
jrepath, sizeof(jrepath),
jvmpath, sizeof(jvmpath),
jvmcfg, sizeof(jvmcfg));
// 记录加载libjvm.so的起始时间,
// 在加载结束后可以得到并打印出加载libjvm.so的耗时
if (!IsJavaArgs()) {
SetJvmEnvironment(argc,argv);
}
ifn.CreateJavaVM = 0;
ifn.GetDefaultJavaVMInitArgs = 0;
if (JLI_IsTraceLauncher()) {
start = CounterGet();
}
/* 加载
/home/wz/openjdk/openjdk-jdk9-jdk9/build/
linux-x86_64-normal-server-slowdebug/
jdk/lib/server/libjvm.so
*/
if (!LoadJavaVM(jvmpath, &ifn)) {
return(6);
}
if (JLI_IsTraceLauncher()) {
end = CounterGet();
}
JLI_TraceLauncher("%ld micro seconds to LoadJavaVM\n",
(long)(jint)Counter2Micros(end-start));
++argv;
--argc;
if (IsJavaArgs()) {
/* Preprocess wrapper arguments */
TranslateApplicationArgs(jargc, jargv, &argc, &argv);
if (!AddApplicationOptions(appclassc, appclassv)) {
return(1);
}
} else {
/* Set default CLASSPATH */
char* cpath = getenv("CLASSPATH");
if (cpath != NULL) {
SetClassPath(cpath);
}
}
/* Parse command line options; if the return value of
* ParseArguments is false, the program should exit.
*/
//解析命令行参数
if (!ParseArguments(&argc, &argv, &mode, &what, &ret, jrepath))
{
return(ret);
}
/* Override class path if -jar flag was specified */
// 如果由-jar参数,重新设置classpath
if (mode == LM_JAR) {
SetClassPath(what); /* Override class path */
}
/* set the -Dsun.java.command pseudo property */
SetJavaCommandLineProp(what, argc, argv);
/* Set the -Dsun.java.launcher pseudo property */
SetJavaLauncherProp();
/* set the -Dsun.java.launcher.* platform properties */
SetJavaLauncherPlatformProps();
return JVMInit(&ifn, threadStackSize, argc, argv, mode, what, ret);
}
如下为加载libjvm.so的函数,该方法初始化了ifn中三个函数指针(CreateJavaVM、GetDefaultJavaVMInitArgs、GetCreatedJavaVMs)的值,指向libjvm.so中对应的方法:
jboolean
LoadJavaVM(const char *jvmpath, InvocationFunctions *ifn)
{
void *libjvm;
JLI_TraceLauncher("JVM path is %s\n", jvmpath);
libjvm = dlopen(jvmpath, RTLD_NOW + RTLD_GLOBAL);
if (libjvm == NULL) {
#if defined(__solaris__) && defined(__sparc) && !defined(_LP64) /* i.e. 32-bit sparc */
FILE * fp;
Elf32_Ehdr elf_head;
int count;
int location;
fp = fopen(jvmpath, "r");
if (fp == NULL) {
JLI_ReportErrorMessage(DLL_ERROR2, jvmpath, dlerror());
return JNI_FALSE;
}
/* read in elf header */
count = fread((void*)(&elf_head), sizeof(Elf32_Ehdr), 1, fp);
fclose(fp);
if (count < 1) {
JLI_ReportErrorMessage(DLL_ERROR2, jvmpath, dlerror());
return JNI_FALSE;
}
/*
* Check for running a server vm (compiled with -xarch=v8plus)
* on a stock v8 processor. In this case, the machine type in
* the elf header would not be included the architecture list
* provided by the isalist command, which is turn is gotten from
* sysinfo. This case cannot occur on 64-bit hardware and thus
* does not have to be checked for in binaries with an LP64 data
* model.
*/
if (elf_head.e_machine == EM_SPARC32PLUS) {
char buf[257]; /* recommended buffer size from sysinfo man
page */
long length;
char* location;
length = sysinfo(SI_ISALIST, buf, 257);
if (length > 0) {
location = JLI_StrStr(buf, "sparcv8plus ");
if (location == NULL) {
JLI_ReportErrorMessage(JVM_ERROR3);
return JNI_FALSE;
}
}
}
#endif
JLI_ReportErrorMessage(DLL_ERROR1, __LINE__);
JLI_ReportErrorMessage(DLL_ERROR2, jvmpath, dlerror());
return JNI_FALSE;
}
ifn->CreateJavaVM = (CreateJavaVM_t)
dlsym(libjvm, "JNI_CreateJavaVM");
if (ifn->CreateJavaVM == NULL) {
JLI_ReportErrorMessage(DLL_ERROR2, jvmpath, dlerror());
return JNI_FALSE;
}
ifn->GetDefaultJavaVMInitArgs = (GetDefaultJavaVMInitArgs_t)
dlsym(libjvm, "JNI_GetDefaultJavaVMInitArgs");
if (ifn->GetDefaultJavaVMInitArgs == NULL) {
JLI_ReportErrorMessage(DLL_ERROR2, jvmpath, dlerror());
return JNI_FALSE;
}
ifn->GetCreatedJavaVMs = (GetCreatedJavaVMs_t)
dlsym(libjvm, "JNI_GetCreatedJavaVMs");
if (ifn->GetCreatedJavaVMs == NULL) {
JLI_ReportErrorMessage(DLL_ERROR2, jvmpath, dlerror());
return JNI_FALSE;
}
return JNI_TRUE;
}
如下为解析命令行的参数,对于-h, -version这种参数,直接返回。
static jboolean
ParseArguments(int *pargc, char ***pargv,
int *pmode, char **pwhat,
int *pret, const char *jrepath)
{
int argc = *pargc;
char **argv = *pargv;
int mode = LM_UNKNOWN;
char *arg;
*pret = 0;
while ((arg = *argv) != 0 && *arg == '-') {
...
/*
* Option to set main entry point
*/
if (JLI_StrCmp(arg, "-jar") == 0) {
ARG_CHECK(argc, ARG_ERROR2, arg);
mode = LM_JAR;
...
/*
* The following cases will cause the argument parsing to stop
*/
} else if (JLI_StrCmp(arg, "-help") == 0 ||
JLI_StrCmp(arg, "-h") == 0 ||
JLI_StrCmp(arg, "-?") == 0) {
printUsage = JNI_TRUE;
return JNI_TRUE;
} else if (JLI_StrCmp(arg, "--help") == 0) {
printUsage = JNI_TRUE;
printTo = USE_STDOUT;
return JNI_TRUE;
} else if (JLI_StrCmp(arg, "-version") == 0) {
printVersion = JNI_TRUE;
return JNI_TRUE;
} else if (JLI_StrCmp(arg, "--version") == 0) {
1.3 JVMInit
接下来调用jdk/src/java.base/unix/native/libjli/java_md_solinux.c/JVMInit方法。
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);
}
该方法继续调用jdk/src/java.base/share/native/libjli/java.c/ContinueInNewThread方法
int
ContinueInNewThread(InvocationFunctions* ifn, jlong threadStackSize,
int argc, char **argv,
int mode, char *what, int ret)
{
/*
* If user doesn't specify stack size, check if VM has a preference.
* Note that HotSpot no longer supports JNI_VERSION_1_1 but it will
* return its default stack size through the init args structure.
*/
if (threadStackSize == 0) {
struct JDK1_1InitArgs args1_1;
memset((void*)&args1_1, 0, sizeof(args1_1));
args1_1.version = JNI_VERSION_1_1;
ifn->GetDefaultJavaVMInitArgs(&args1_1); /* ignore return value */
if (args1_1.javaStackSize > 0) {
threadStackSize = args1_1.javaStackSize;
}
}
{ /* 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;
}
}
该方法继续调用jdk/src/java.base/unix/native/libjli/java_md_solinux.c/ContinueInNewThread0
/*
* Block current thread and continue execution in a new thread
*/
int
ContinueInNewThread0(int (JNICALL *continuation)(void *), jlong stack_size, void * args) {
int rslt;
#ifndef __solaris__
pthread_t tid;
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
// 如果指定了线程栈大小,设置到属性中
if (stack_size > 0) {
pthread_attr_setstacksize(&attr, stack_size);
}
if (pthread_create(&tid, &attr, (void *(*)(void*))continuation, (void*)args) == 0) {
void * tmp;
// 子线程创建成功后,当前线程在此以阻塞方式等待子线程结束
pthread_join(tid, &tmp);
rslt = (int)(intptr_t)tmp;
} else {
/*
* Continue execution in current thread if for some reason (e.g. out of
* memory/LWP) a new thread can't be created. This will likely fail
* later in continuation as JNI_CreateJavaVM needs to create quite a
* few new threads, anyway, just give it a try..
*/
// 如果线程创建失败,则直接在当前线程尝试执行
rslt = continuation(args);
}
pthread_attr_destroy(&attr);
#else /* __solaris__ */
thread_t tid;
long flags = 0;
if (thr_create(NULL, stack_size, (void *(*)(void *))continuation, args, flags, &tid) == 0) {
void * tmp;
thr_join(tid, NULL, &tmp);
rslt = (int)(intptr_t)tmp;
} else {
/* See above. Continue in current thread if thr_create() failed */
rslt = continuation(args);
}
#endif /* !__solaris__ */
return rslt;
}
线程执行栈如下所示:
可以看到新线程入口函数为JavaMain
2.执行java代码的新线程(JavaMain)
2.1 JavaMain
位于jdk/src/java.base/share/native/libjli/java.c/JavaMain方法中:
int JNICALL
JavaMain(void * _args)
{
JavaMainArgs *args = (JavaMainArgs *)_args;
int argc = args->argc;
char **argv = args->argv;
int mode = args->mode;
char *what = args->what;
InvocationFunctions ifn = args->ifn;
JavaVM *vm = 0;
JNIEnv *env = 0;
jclass mainClass = NULL;
jclass appClass = NULL; // actual application class being launched
jmethodID mainID;
jobjectArray mainArgs;
int ret = 0;
jlong start, end;
RegisterThread();
/* Initialize the virtual machine */
// 记录当前时间,统计JVM初始化耗时的时候用到
start = CounterGet();
// 调用libjvm.so库中的CreateJavaVM方法初始化虚拟机
if (!InitializeJVM(&vm, &env, &ifn)) {
JLI_ReportErrorMessage(JVM_ERROR1);
exit(1);
}
// 调用java类的sun.launcher.LauncherHelper.showSettins方法
// 打印jvm设置信息
if (showSettings != NULL) {
ShowSettings(env, showSettings);
CHECK_EXCEPTION_LEAVE(1);
}
// show resolved modules and continue
/*
调用java类的静态方法(sun.misc.Version.print),打印:
1.java版本信息
2.java运行时版本信息
3.java虚拟机版本信息
*/
if (showResolvedModules) {
ShowResolvedModules(env);
CHECK_EXCEPTION_LEAVE(1);
}
// list observable modules, then exit
if (listModules) {
ListModules(env);
CHECK_EXCEPTION_LEAVE(1);
LEAVE();
}
// describe a module, then exit
if (describeModule != NULL) {
DescribeModule(env, describeModule);
CHECK_EXCEPTION_LEAVE(1);
LEAVE();
}
// validate modules on the module path, then exit
if (validateModules) {
jboolean okay = ValidateModules(env);
CHECK_EXCEPTION_LEAVE(1);
if (!okay) ret = 1;
LEAVE();
}
if (printVersion || showVersion) {
PrintJavaVersion(env, showVersion);
CHECK_EXCEPTION_LEAVE(0);
if (printVersion) {
LEAVE();
}
}
/* If the user specified neither a class name nor a JAR file */
if (printXUsage || printUsage || what == 0 || mode == LM_UNKNOWN) {
PrintUsage(env, printXUsage);
CHECK_EXCEPTION_LEAVE(1);
LEAVE();
}
FreeKnownVMs(); /* after last possible PrintUsage */
if (JLI_IsTraceLauncher()) {
end = CounterGet();
JLI_TraceLauncher("%ld micro seconds to InitializeJVM\n",
(long)(jint)Counter2Micros(end-start));
}
/* At this stage, argc/argv have the application's arguments */
if (JLI_IsTraceLauncher()){
int i;
printf("%s is '%s'\n", launchModeNames[mode], what);
printf("App's argc is %d\n", argc);
for (i=0; i < argc; i++) {
printf(" argv[%2d] = '%s'\n", i, argv[i]);
}
}
ret = 1;
/*
* Get the application's main class. It also checks if the main
* method exists.
*
* See bugid 5030265. The Main-Class name has already been parsed
* from the manifest, but not parsed properly for UTF-8 support.
* Hence the code here ignores the value previously extracted and
* uses the pre-existing code to reextract the value. This is
* possibly an end of release cycle expedient. However, it has
* also been discovered that passing some character sets through
* the environment has "strange" behavior on some variants of
* Windows. Hence, maybe the manifest parsing code local to the
* launcher should never be enhanced.
*
* Hence, future work should either:
* 1) Correct the local parsing code and verify that the
* Main-Class attribute gets properly passed through
* all environments,
* 2) Remove the vestages of maintaining main_class through
* the environment (and remove these comments).
*
* This method also correctly handles launching existing JavaFX
* applications that may or may not have a Main-Class manifest entry.
*/
// 获取Java程序的启动类
mainClass = LoadMainClass(env, mode, what);
CHECK_EXCEPTION_NULL_LEAVE(mainClass);
/*
* In some cases when launching an application that needs a helper, e.g., a
* JavaFX application with no main method, the mainClass will not be the
* applications own main class but rather a helper class. To keep things
* consistent in the UI we need to track and report the application main class.
*/
appClass = GetApplicationClass(env);
NULL_CHECK_RETURN_VALUE(appClass, -1);
/* Build platform specific argument array */
mainArgs = CreateApplicationArgs(env, argv, argc);
CHECK_EXCEPTION_NULL_LEAVE(mainArgs);
if (dryRun) {
ret = 0;
LEAVE();
}
/*
* PostJVMInit uses the class name as the application name for GUI purposes,
* for example, on OSX this sets the application name in the menu bar for
* both SWT and JavaFX. So we'll pass the actual application class here
* instead of mainClass as that may be a launcher or helper class instead
* of the application class.
*/
PostJVMInit(env, appClass, vm);
CHECK_EXCEPTION_LEAVE(1);
/*
* The LoadMainClass not only loads the main class, it will also ensure
* that the main method's signature is correct, therefore further checking
* is not required. The main method is invoked here so that extraneous java
* stacks are not in the application stack trace.
*/
// 获取Java程序的启动方法
mainID = (*env)->GetStaticMethodID(env, mainClass, "main",
"([Ljava/lang/String;)V");
CHECK_EXCEPTION_NULL_LEAVE(mainID);
/* Invoke main method. */
// 执行Java程序的main方法
(*env)->CallStaticVoidMethod(env, mainClass, mainID, mainArgs);
/*
* The launcher's exit code (in the absence of calls to
* System.exit) will be non-zero if main threw an exception.
*/
ret = (*env)->ExceptionOccurred(env) == NULL ? 0 : 1;
LEAVE();
}
2.2 InitializeJVM
位于jdk/src/java.base/share/native/libjli/java.c/
初始化虚拟机InitializeJVM,调用ifn->CreateJavaVM:
/*
* 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 %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;
}
继续调用hotspot/src/share/vm/prims/jni.cpp中的CreateJavaVM方法:
_JNI_IMPORT_OR_EXPORT_ jint JNICALL JNI_CreateJavaVM(JavaVM **vm, void **penv, void *args) {
jint result = 0;
// On Windows, let CreateJavaVM run with SEH protection
#ifdef _WIN32
__try {
#endif
result = JNI_CreateJavaVM_inner(vm, penv, args);
#ifdef _WIN32
} __except(topLevelExceptionFilter((_EXCEPTION_POINTERS*)_exception_info())) {
// Nothing to do.
}
#endif
return result;
}
继续调用hotspot/src/share/vm/prims/jni.cpp中的JNI_CreateJavaVM_inner方法:
static jint JNI_CreateJavaVM_inner(JavaVM **vm, void **penv, void *args) {
result = Threads::create_vm((JavaVMInitArgs*) args, &can_try_again);
if (result == JNI_OK) {
JavaThread *thread = JavaThread::current();
assert(!thread->has_pending_exception(), "should have returned not OK");
/* thread is thread_in_vm here */
*vm = (JavaVM *)(&main_vm);
*(JNIEnv**)penv = thread->jni_environment();
继续调用hotspot/src/share/vm/runtime/thread.cpp/ Threads::create_vm方法:
...
// Parse arguments
jint parse_result = Arguments::parse(args);
...
// Initialize global data structures and create system classes in heap
vm_init_globals();
...
// Initialize global modules
// 包含建堆并设置GC
jint status = init_globals();
...
// 创建VM Thread
Thread* vmthread = VMThread::vm_thread();
...
// 新建了Reference Handl线程和Finalizer线程
initialize_java_lang_classes(main_thread, CHECK_JNI_ERR);
...
2.3 执行Java程序CallStaticVoidMethod
位于hotspot/src/share/vm/prims/jni.cpp
JNI_ENTRY(void, jni_CallStaticVoidMethod(JNIEnv *env, jclass cls, jmethodID methodID, ...))
JNIWrapper("CallStaticVoidMethod");
HOTSPOT_JNI_CALLSTATICVOIDMETHOD_ENTRY(env, cls, (uintptr_t) methodID);
DT_VOID_RETURN_MARK(CallStaticVoidMethod);
va_list args;
va_start(args, methodID);
JavaValue jvalue(T_VOID);
JNI_ArgumentPusherVaArg ap(methodID, args);
jni_invoke_static(env, &jvalue, NULL, JNI_STATIC, methodID, &ap, CHECK);
va_end(args);
JNI_END
调用jni_invoke_static
位于位于hotspot/src/share/vm/prims/jni.cpp
static void jni_invoke_static(JNIEnv *env, JavaValue* result, jobject receiver, JNICallType call_type, jmethodID method_id, JNI_ArgumentPusher *args, TRAPS) {
methodHandle method(THREAD, Method::resolve_jmethod_id(method_id));
// Create object to hold arguments for the JavaCall, and associate it with
// the jni parser
ResourceMark rm(THREAD);
int number_of_parameters = method->size_of_parameters();
JavaCallArguments java_args(number_of_parameters);
args->set_java_argument_object(&java_args);
assert(method->is_static(), "method should be static");
// Fill out JavaCallArguments object
args->iterate( Fingerprinter(method).fingerprint() );
// Initialize result type
result->set_type(args->get_ret_type());
// Invoke the method. Result is returned as oop.
JavaCalls::call(result, method, &java_args, CHECK);
// Convert result
if (result->get_type() == T_OBJECT || result->get_type() == T_ARRAY) {
result->set_jobject(JNIHandles::make_local(env, (oop) result->get_jobject()));
}
}
调用JavaCalls::call
位于hotspot/src/share/vm/runtime/javaCalls.cpp
void JavaCalls::call(JavaValue* result, const methodHandle& method, JavaCallArguments* args, TRAPS) {
// Check if we need to wrap a potential OS exception handler around thread
// This is used for e.g. Win32 structured exception handlers
assert(THREAD->is_Java_thread(), "only JavaThreads can make JavaCalls");
// Need to wrap each and every time, since there might be native code down the
// stack that has installed its own exception handlers
os::os_exception_wrapper(call_helper, result, method, args, THREAD);
}
调用os::os_exception_wrapper
位于hotspot/src/os/linux/vm/os_linux.cpp
// This does not do anything on Linux. This is basically a hook for being
// able to use structured exception handling (thread-local exception filters)
// on, e.g., Win32.
void
os::os_exception_wrapper(java_call_t f, JavaValue* value, const methodHandle& method,
JavaCallArguments* args, Thread* thread) {
f(value, method, args, thread);
}
调用JavaCalls::call_helper
位于hotspot/src/share/vm/runtime/javaCalls.cpp
void JavaCalls::call_helper(JavaValue* result, const methodHandle& method, JavaCallArguments* args, TRAPS) {
// During dumping, Java execution environment is not fully initialized. Also, Java execution
// may cause undesirable side-effects in the class metadata.
...
// do call
{ JavaCallWrapper link(method, receiver, result, CHECK);
{ HandleMark hm(thread); // HandleMark used by HandleMarkCleaner
StubRoutines::call_stub()(
(address)&link,
// (intptr_t*)&(result->_value), // see NOTE above (compiler problem)
result_val_address, // see NOTE above (compiler problem)
result_type,
method(),
entry_point,
args->parameters(),
args->size_of_parameters(),
CHECK
);
result = link.result(); // circumvent MS C++ 5.0 compiler bug (result is clobbered across call)
// Preserve oop return value across possible gc points
if (oop_result_flag) {
thread->set_vm_result((oop) result->get_jobject());
}
}
} // Exit JavaCallWrapper (can block - potential return oop must be preserved)
...
}
运行到这里的时候,可以看到Java程序运行完成:
2.4 LEAVE()
/*
* Always detach the main thread so that it appears to have ended when
* the application's main method exits. This will invoke the
* uncaught exception handler machinery if main threw an
* exception. An uncaught exception handler cannot change the
* launcher's return code except by calling System.exit.
*
* Wait for all non-daemon threads to end, then destroy the VM.
* This will actually create a trivial new Java waiter thread
* named "DestroyJavaVM", but this will be seen as a different
* thread from the one that executed main, even though they are
* the same C thread. This allows mainThread.join() and
* mainThread.isAlive() to work as expected.
*/
#define LEAVE() \
do { \
if ((*vm)->DetachCurrentThread(vm) != JNI_OK) { \
JLI_ReportErrorMessage(JVM_ERROR2); \
ret = 1; \
} \
if (JNI_TRUE) { \
(*vm)->DestroyJavaVM(vm); \
return ret; \
} \
} while (JNI_FALSE)
调用DetachCurrentThread主线程,因为应用的main方法已经退出了。等待所有非守护线程执行完成,然后调用DestroyJavaVM执行销毁。
参考
- 《Java虚拟机精讲》
- 修改,编译,GDB调试openjdk8源码(docker环境下)