JVM源代码是一个巨大的焦油坑。
├─agent Serviceability Agent的实现
├─make 用来build出HotSpot的各种配置文件
├─src HotSpot VM的源代码
│ ├─cpu CPU相关代码
│ ├─os 操作系相关代码
│ ├─os_cpu 操作系统+CPU的组合相关的代码
│ └─share 平台无关的共通代码
│ ├─tools 工具
│ │ ├─hsdis 反汇编插件
│ │ ├─IdealGraphVisualizer 将server编译器的中间代码可视化的工具
│ │ ├─launcher 启动程序“java”
│ │ ├─LogCompilation 将-XX:+LogCompilation输出的日志(hotspot.log)整理成更容易阅读的格式的工具
│ │ └─ProjectCreator 生成Visual Studio的project文件的工具
│ └─vm HotSpot VM的核心代码
│ ├─adlc 平台描述文件(上面的cpu或os_cpu里的*.ad文件)的编译器
│ ├─asm 汇编器接口
│ ├─c1 client编译器
│ ├─ci 动态编译器的公共服务/接口
│ ├─classfile 类文件的处理(包括类加载和系统符号表等)
│ ├─code 动态生成的代码的管理
│ ├─compiler 编译器接口
│ ├─gc_implementation GC的实现
│ │ ├─concurrentMarkSweep Concurrent Mark Sweep GC的实现
│ │ ├─g1 Garbage-First GC的实现(不使用老的分代式GC框架)
│ │ ├─parallelScavenge ParallelScavenge GC的实现(server VM默认,不使用老的分代式GC框架)
│ │ ├─parNew ParNew GC的实现
│ │ └─shared GC的共通实现
│ ├─gc_interface GC的接口
│ ├─interpreter 解释器,包括“模板解释器”(官方版在用)和“C++解释器”(官方版不在用)
│ ├─libadt 一些抽象数据结构
│ ├─memory 内存管理相关(老的分代式GC框架也在这里)
│ ├─oops HotSpot VM的对象系统的实现
│ ├─opto server编译器
│ ├─prims HotSpot VM的对外接口,包括部分标准库的native部分和JVMTI实现
│ ├─runtime 运行时支持库(包括线程管理、编译器调度、锁、反射等)
│ ├─services 主要是用来支持JMX之类的管理功能的接口
│ ├─shark 基于LLVM的JIT编译器(官方版里没有使用)
│ └─utilities 一些基本的工具类
└─test 单元测试
来源https://hllvm-group.iteye.com/group/topic/26998#193368 RednaxelaFX大神。
int
main(int argc, char ** argv)
{
char *jarfile = 0;
char *classname = 0;
char *s = 0;
char *main_class = NULL;
int ret;
InvocationFunctions ifn;
jlong start, end;
char jrepath[MAXPATHLEN], jvmpath[MAXPATHLEN];
char ** original_argv = argv;
if (getenv("_JAVA_LAUNCHER_DEBUG") != 0) {
_launcher_debug = JNI_TRUE;
printf("----_JAVA_LAUNCHER_DEBUG----\n");
}
/* copy original argv */
{
int i;
original_argv = (char**)JLI_MemAlloc(sizeof(char*)*(argc+1));
for(i = 0; i < argc+1; i++)
original_argv[i] = argv[i];
}
//查找JRE的路径,加载自己的动态连接库。
CreateExecutionEnvironment(&argc, &argv,
jrepath, sizeof(jrepath),
jvmpath, sizeof(jvmpath),
original_argv);
printf("Using java runtime at: %s\n", jrepath);
ifn.CreateJavaVM = 0;
ifn.GetDefaultJavaVMInitArgs = 0;
if (_launcher_debug)
start = CounterGet();
if (!LoadJavaVM(jvmpath, &ifn)) {
exit(6);
}
if (_launcher_debug) {
end = CounterGet();
printf("%ld micro seconds to LoadJavaVM\n",
(long)(jint)Counter2Micros(end-start));
}
#ifdef JAVA_ARGS /* javac, jar and friends. */
progname = "java";
#else /* java, oldjava, javaw and friends */
#ifdef PROGNAME
progname = PROGNAME;
#else
progname = *argv;
if ((s = strrchr(progname, FILE_SEPARATOR)) != 0) {
progname = s + 1;
}
#endif /* PROGNAME */
#endif /* JAVA_ARGS */
++argv;
--argc;
#ifdef JAVA_ARGS
/* Preprocess wrapper arguments */
TranslateApplicationArgs(&argc, &argv);
if (!AddApplicationOptions()) {
exit(1);
}
#endif
/* Set default CLASSPATH */
if ((s = getenv("CLASSPATH")) == 0) {
s = ".";
}
#ifndef JAVA_ARGS
SetClassPath(s);
#endif
/*
* Parse command line options; if the return value of
* ParseArguments is false, the program should exit.
*/
if (!ParseArguments(&argc, &argv, &jarfile, &classname, &ret, jvmpath)) {
exit(ret);
}
/* Override class path if -jar flag was specified */
if (jarfile != 0) {
SetClassPath(jarfile);
}
/* set the -Dsun.java.command pseudo property */
SetJavaCommandLineProp(classname, jarfile, argc, argv);
/* Set the -Dsun.java.launcher pseudo property */
SetJavaLauncherProp();
/* set the -Dsun.java.launcher.* platform properties */
SetJavaLauncherPlatformProps();
#ifndef GAMMA
/* Show the splash screen if needed */
ShowSplashScreen();
#endif
/*
* Done with all command line processing and potential re-execs so
* clean up the environment.
*/
(void)UnsetEnv(ENV_ENTRY);
#ifndef GAMMA
(void)UnsetEnv(SPLASH_FILE_ENV_ENTRY);
(void)UnsetEnv(SPLASH_JAR_ENV_ENTRY);
JLI_MemFree(splash_jar_entry);
JLI_MemFree(splash_file_entry);
#endif
/*
* 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.
*/
//设置JVM线程堆栈的大小
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;
}
}
//开始准备执行用户提交的JAVA代码的main方法
{ /* Create a new thread to create JVM and invoke main method */
struct JavaMainArgs args;
args.argc = argc;
args.argv = argv;
args.jarfile = jarfile;
args.classname = classname;
args.ifn = ifn;
return ContinueInNewThread(JavaMain, threadStackSize, (void*)&args);
}
}
int JNICALL
JavaMain(void * _args)
{
struct JavaMainArgs *args = (struct JavaMainArgs *)_args;
int argc = args->argc;
char **argv = args->argv;
char *jarfile = args->jarfile;
char *classname = args->classname;
InvocationFunctions ifn = args->ifn;
JavaVM *vm = 0;
JNIEnv *env = 0;
jstring mainClassName;
jclass mainClass;
jmethodID mainID;
jobjectArray mainArgs;
int ret = 0;
jlong start, end;
/*
* Error message to print or display; by default the message will
* only be displayed in a window.
*/
char * message = "Fatal exception occurred. Program will exit.";
jboolean messageDest = JNI_FALSE;
/* Initialize the virtual machine */
if (_launcher_debug)
start = CounterGet();
if (!InitializeJVM(&vm, &env, &ifn)) {
ReportErrorMessage("Could not create the Java virtual machine.",
JNI_TRUE);
exit(1);
}
if (printVersion || showVersion) {
PrintJavaVersion(env);
if ((*env)->ExceptionOccurred(env)) {
ReportExceptionDescription(env);
goto leave;
}
if (printVersion) {
ret = 0;
message = NULL;
goto leave;
}
if (showVersion) {
fprintf(stderr, "\n");
}
}
/* If the user specified neither a class name nor a JAR file */
if (jarfile == 0 && classname == 0) {
PrintUsage();
message = NULL;
goto leave;
}
#ifndef GAMMA
FreeKnownVMs(); /* after last possible PrintUsage() */
#endif
if (_launcher_debug) {
end = CounterGet();
printf("%ld micro seconds to InitializeJVM\n",
(long)(jint)Counter2Micros(end-start));
}
/* At this stage, argc/argv have the applications' arguments */
if (_launcher_debug) {
int i = 0;
printf("Main-Class is '%s'\n", classname ? classname : "");
printf("Apps' argc is %d\n", argc);
for (; i < argc; i++) {
printf(" argv[%2d] = '%s'\n", i, argv[i]);
}
}
ret = 1;
/*
* Get the application's main class.
*
* 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).
*/
//寻找MainClass
if (jarfile != 0) {
mainClassName = GetMainClassName(env, jarfile);
if ((*env)->ExceptionOccurred(env)) {
ReportExceptionDescription(env);
goto leave;
}
if (mainClassName == NULL) {
const char * format = "Failed to load Main-Class manifest "
"attribute from\n%s";
message = (char*)JLI_MemAlloc((strlen(format) + strlen(jarfile)) *
sizeof(char));
sprintf(message, format, jarfile);
messageDest = JNI_TRUE;
goto leave;
}
classname = (char *)(*env)->GetStringUTFChars(env, mainClassName, 0);
if (classname == NULL) {
ReportExceptionDescription(env);
goto leave;
}
mainClass = LoadClass(env, classname);
if(mainClass == NULL) { /* exception occured */
const char * format = "Could not find the main class: %s. Program will exit.";
ReportExceptionDescription(env);
message = (char *)JLI_MemAlloc((strlen(format) +
strlen(classname)) * sizeof(char) );
messageDest = JNI_TRUE;
sprintf(message, format, classname);
goto leave;
}
(*env)->ReleaseStringUTFChars(env, mainClassName, classname);
} else {
mainClassName = NewPlatformString(env, classname);
if (mainClassName == NULL) {
const char * format = "Failed to load Main Class: %s";
message = (char *)JLI_MemAlloc((strlen(format) + strlen(classname)) *
sizeof(char) );
sprintf(message, format, classname);
messageDest = JNI_TRUE;
goto leave;
}
classname = (char *)(*env)->GetStringUTFChars(env, mainClassName, 0);
if (classname == NULL) {
ReportExceptionDescription(env);
goto leave;
}
mainClass = LoadClass(env, classname);//加载主类的实际方法
if(mainClass == NULL) { /* exception occured */
const char * format = "Could not find the main class: %s. Program will exit.";
ReportExceptionDescription(env);
message = (char *)JLI_MemAlloc((strlen(format) +
strlen(classname)) * sizeof(char) );
messageDest = JNI_TRUE;
sprintf(message, format, classname);
goto leave;
}
(*env)->ReleaseStringUTFChars(env, mainClassName, classname);
}
/* Get the application's main method */
//获取Main方法
mainID = (*env)->GetStaticMethodID(env, mainClass, "main",
"([Ljava/lang/String;)V");
if (mainID == NULL) {
if ((*env)->ExceptionOccurred(env)) {
ReportExceptionDescription(env);
} else {
message = "No main method found in specified class.";
messageDest = JNI_TRUE;
}
goto leave;
}
{ /* Make sure the main method is public */
jint mods;
jmethodID mid;
jobject obj = (*env)->ToReflectedMethod(env, mainClass,
mainID, JNI_TRUE);
if( obj == NULL) { /* exception occurred */
ReportExceptionDescription(env);
goto leave;
}
mid =
(*env)->GetMethodID(env,
(*env)->GetObjectClass(env, obj),
"getModifiers", "()I");
if ((*env)->ExceptionOccurred(env)) {
ReportExceptionDescription(env);
goto leave;
}
mods = (*env)->CallIntMethod(env, obj, mid);
if ((mods & 1) == 0) { /* if (!Modifier.isPublic(mods)) ... */
message = "Main method not public.";
messageDest = JNI_TRUE;
goto leave;
}
}
/* Build argument array */
mainArgs = NewPlatformStringArray(env, argv, argc);
if (mainArgs == NULL) {
ReportExceptionDescription(env);
goto leave;
}
/* Invoke main method. */
//调用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;
/*
* 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.
*/
if ((*vm)->DetachCurrentThread(vm) != 0) {
message = "Could not detach main thread.";
messageDest = JNI_TRUE;
ret = 1;
goto leave;
}
message = NULL;
leave:
/*
* 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.
*/
(*vm)->DestroyJavaVM(vm);
if(message != NULL && !noExitErrorMessage)
ReportErrorMessage(message, messageDest);
return ret;
}