Java源码探秘:JVM启动与类加载机制
JVM初始化简介
前言
本文旨在记录近期研读Java源码的学习心得与疑难问题。由于个人理解水平有限,文中内容可能存在疏漏,恳请读者不吝指正。
JVM初始化简介
在JVM启动过程时,会在JVMInit()函数中进行JVM初始化,并创建创建一个新的线程来执行java应用的启动过程,就是continuation所指向的JavaMain()函数里面的逻辑。java.c中的JavaMain()函数的核心四个步骤如下:
1. 初始化 JVM (InitializeJVM)
这是最重头戏的部分。它会调用 JNI_CreateJavaVM,这个调用会:
-
分配堆内存:根据你设置的
-Xms和-Xmx划分内存。 -
启动引导类加载器 (Bootstrap ClassLoader):加载
rt.jar或 Java 9 之后的模块化核心类。 -
启动关键系统线程:如垃圾回收线程 (GC threads)、信号转发线程 (Signal Dispatcher) 和编译线程 (JIT Compiler)。
2. 加载主类 (Load Main Class)
JVM 启动后,它需要知道从哪里开始运行。
-
如果你运行的是
java MyClass,它会寻找MyClass.class。 -
如果你运行的是
java -jar app.jar,它会读取MANIFEST.MF文件中的Main-Class属性。
3. 寻找并验证 main 方法
JVM 会在主类中寻找符合特定签名的入口: public static void main(String[] args) 如果找不到,或者签名不对(比如不是 static 的),程序就会在这里抛出经典的 Main method not found 错误并退出。
4. 调用 main 方法
最后,通过 JNI 调用 CallStaticVoidMethod。 至此,控制权正式从 C 语言的启动器移交给你的 Java 代码。
5. JavaMain函数核心代码
jdk\src\share\bin\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;
// 省略部分代码
if (!InitializeJVM(&vm, &env, &ifn)) {
JLI_ReportErrorMessage(JVM_ERROR1);
exit(1);
}
// 省略部分代码
// 加载主类
mainClass = LoadMainClass(env, mode, what);
CHECK_EXCEPTION_NULL_LEAVE(mainClass);
// 省略部分代码
// 调用public static void main(String[] args)方法执行Java应用的逻辑
mainID = (*env)->GetStaticMethodID(env, mainClass, "main", "([Ljava/lang/String;)V");
CHECK_EXCEPTION_NULL_LEAVE(mainID);
(*env)->CallStaticVoidMethod(env, mainClass, mainID, mainArgs);
ret = (*env)->ExceptionOccurred(env) == NULL ? 0 : 1;
LEAVE();
}
启动引导类加载器 (Bootstrap ClassLoader)源码分析
LoadMainClass()函数源码分析
java.c中 LoadMainClass()函数核心代码如下:
static jclass LoadMainClass(JNIEnv *env, int mode, char *name)
{
jmethodID mid;
jstring str;
jobject result;
jlong start, end;
jclass cls = GetLauncherHelperClass(env);
// 省略部分代码
NULL_CHECK0(mid = (*env)->GetStaticMethodID(env, cls,
"checkAndLoadMain",
"(ZILjava/lang/String;)Ljava/lang/Class;"));
str = NewPlatformString(env, name);
CHECK_JNI_RETURN_0(
result = (*env)->CallStaticObjectMethod(
env, cls, mid, USE_STDERR, mode, str));
// 省略部分代码
return (jclass)result;
}
1、在GetLauncherHelperClass()函数中,最终通过JNI执行函数JVM_FindClassFromBootLoader()函数中查找到Java类sun.launcher.LauncherHelper。
java.c中 GetLauncherHelperClass()核心代码如下:
jclass GetLauncherHelperClass(JNIEnv *env)
{
if (helperClass == NULL) {
NULL_CHECK0(helperClass = FindBootStrapClass(env,
"sun/launcher/LauncherHelper"));
}
return helperClass;
}
jdk\src\solaris\bin\java_md_common.c中 FindBootStrapClass()代码。通过JNI调用libjvm.so中的JVM_FindClassFromBootLoader()。
jclass FindBootStrapClass(JNIEnv *env, const char* classname)
{
if (findBootClass == NULL) {
findBootClass = (FindClassFromBootLoader_t *)dlsym(RTLD_DEFAULT,
"JVM_FindClassFromBootLoader");
if (findBootClass == NULL) {
JLI_ReportErrorMessage(DLL_ERROR4,
"JVM_FindClassFromBootLoader");
return NULL;
}
}
return findBootClass(env, classname);
}
JVM_FindClassFromBootLoader源码参见补充说明部分章节JVM_FindClassFromBootLoader源码分析
2、在查找的Java类sun.launcher.LauncherHelper后,调用JNIEnv->CallStaticObjectMethod()函数,执行Java类LauncherHelper中checkAndLoadMain()方法。在该方法中调用java.lang.ClassLoader类中initSystemClassLoader()方法,创建ExtClassLoader和AppClassLoader对象并将AppClassLoader对象返回,后续使用AppClassLoader对象的loadClass()方法完成Java类的查找和加载。
jdk\src\share\classes\sun\launcher\LauncherHelper.java中方法 checkAndLoadMain()核心代码如下:
private static final ClassLoader scloader = ClassLoader.getSystemClassLoader();
public static Class<?> checkAndLoadMain(boolean printToStderr,
int mode,
String what) {
initOutput(printToStderr);
// get the class name
String cn = null;
switch (mode) {
case LM_CLASS:
cn = what;
break;
case LM_JAR:
cn = getMainClassFromJar(what);
break;
default:
// should never happen
throw new InternalError("" + mode + ": Unknown launch mode");
}
cn = cn.replace('/', '.');
Class<?> mainClass = null;
try {
mainClass = scloader.loadClass(cn);
} catch (NoClassDefFoundError | ClassNotFoundException cnfe) {
// 省略部分代码
}
// 省略部分代码
return mainClass;
}
jdk\src\share\classes\java\lang\ClassLoader.java中方法 getSystemClassLoader() 该方法中会获取sun.misc.Launcher类中创建的ClassLoader(ExtClassLoader和AppClassLoader),其核心代码如下:
public static ClassLoader getSystemClassLoader() {
initSystemClassLoader();
if (scl == null) {
return null;
}
// 省略部分代码
return scl;
}
private static synchronized void initSystemClassLoader() {
if (!sclSet) {
// 省略部分代码
sun.misc.Launcher l = sun.misc.Launcher.getLauncher();
if (l != null) {
scl = l.getClassLoader();
}
sclSet = true;
}
}
jdk\src\share\classes\sun\misc\Launcher.java,创建ExtClassLoader和AppClassLoader。
public class Launcher {
// 省略部分代码
private static Launcher launcher = new Launcher();
public static Launcher getLauncher() {
return launcher;
}
private ClassLoader loader;
public Launcher() {
// Create the extension class loader
ClassLoader extcl;
try {
extcl = ExtClassLoader.getExtClassLoader();
} catch (IOException e) {
// 省略异常处理部分代码
}
// Now create the class loader to use to launch the application
try {
loader = AppClassLoader.getAppClassLoader(extcl);
} catch (IOException e) {
// 省略异常处理部分代码
}
// Also set the context class loader for the primordial thread.
Thread.currentThread().setContextClassLoader(loader);
}
public ClassLoader getClassLoader() {
return loader;
}
}
补充说明
JVM_FindClassFromBootLoader源码分析
1、hotspot\src\share\vm\prims\jvm.cpp中 JVM_FindClassFromBootLoader()代码如下:
JVM_ENTRY(jclass, JVM_FindClassFromBootLoader(JNIEnv* env,
const char* name))
JVMWrapper2("JVM_FindClassFromBootLoader %s", name);
// Java libraries should ensure that name is never null...
if (name == NULL || (int)strlen(name) > Symbol::max_length()) {
// It's impossible to create this class; the name cannot fit
// into the constant pool.
return NULL;
}
TempNewSymbol h_name = SymbolTable::new_symbol(name, CHECK_NULL);
Klass* k = SystemDictionary::resolve_or_null(h_name, CHECK_NULL);
if (k == NULL) {
return NULL;
}
if (TraceClassResolution) {
trace_class_resolution(k);
}
return (jclass) JNIHandles::make_local(env, k->java_mirror());
JVM_END
2、宏展开后的代码如下:
extern "C" {
jclass JNICALL JVM_FindClassFromBootLoader(JNIEnv* env, const char* name) {
JavaThread* thread=JavaThread::thread_from_jni_environment(env);
ThreadInVMfromNative __tiv(thread);
debug_only(VMNativeEntryWrapper __vew;);
/* VM_ENTRY_BASE 展开内容 */
TRACE_CALL(jclass, JVM_FindClassFromBootLoader)
HandleMarkCleaner __hm(thread);
Thread* THREAD = thread;
os::verify_stack_alignment();
/* 原始函数体开始 */
JVMWrapper2("JVM_FindClassFromBootLoader %s", name);
if (name == NULL || (int)strlen(name) > Symbol::max_length()) {
return NULL;
}
TempNewSymbol h_name = SymbolTable::new_symbol(name, CHECK_NULL);
Klass* k = SystemDictionary::resolve_or_null(h_name, CHECK_NULL);
if (k == NULL) {
return NULL;
}
if (TraceClassResolution) {
trace_class_resolution(k);
}
return (jclass) JNIHandles::make_local(env, k->java_mirror());
}
} // extern "C" 闭合
3、hotspot\src\share\vm\runtime\thread.hpp中 **thread_from_jni_environment()**核心代码。通过JNIEnv获取其所属的JavaThread。
static JavaThread* thread_from_jni_environment(JNIEnv* env) {
JavaThread *thread_from_jni_env = (JavaThread*)((intptr_t)env - in_bytes(jni_environment_offset()));
// Only return NULL if thread is off the thread list; starting to
// exit should not return NULL.
if (thread_from_jni_env->is_terminated()) {
thread_from_jni_env->block_if_vm_exited();
return NULL;
} else {
return thread_from_jni_env;
}
}
static ByteSize jni_environment_offset() {
return byte_offset_of(JavaThread, _jni_environment);
}
#define byte_offset_of(klass,field) in_ByteSize((int)offset_of(klass, field))
4、hotspot\src\share\vm\utilities\globalDefinitions_gcc.hpp核心代码。获取klass中field的偏移量,方便后续通过klass的首地址+偏移量方式获取field的内存地址。
inline ByteSize in_ByteSize(int size) { return size; }
#define offset_of(klass,field) (size_t)((intx)&(((klass*)16)->field) - 16)
上述代码中 offset_of()作用:在编译时计算一个类(class)或结构体(struct)中某个成员变量(field)相对于对象起始地址的偏移量(Offset)。
代码((klass)16)->field)中使用16而不是0的原因如下:*
在标准的 C 语言 offsetof 宏中,通常使用 (klass*)0。但 OpenJDK 经常使用 16(或者其他非零值),原因如下:
-
规避空指针检测(NULL Pointer Detection): 有些编译器或静态检查工具在看到
(klass*)0并进行解引用(->field)时,会触发“对空指针解引用”的警告或错误,即使这只是在编译期计算地址。使用16可以巧妙地绕过这种检查。 -
处理虚函数表(vtable)或特殊对齐: 在某些平台上,地址
0是受保护的,或者地址0到16之间可能存在特殊的对齐限制。选择16这种稍微偏移一点的地址,通常是为了确保地址计算在语义上是“安全”的。
5、hotspot\src\share\vm\runtime\thread.hpp
class JavaThread: public Thread {
friend class VMStructs;
private:
JavaThread* _next; // The next thread in the Threads list
oop _threadObj; // The Java level thread object
// 省略部分代码
JavaFrameAnchor _anchor; // Encapsulation of current java frame and it state
ThreadFunction _entry_point;
// _jni_environment变量定义
JNIEnv _jni_environment;
}
java启动过程主要源码
1、jdk\src\share\bin\java.h
#include "java.h"
// 省略部分代码
int JVMInit(InvocationFunctions* ifn, jlong threadStackSize,
int argc, char **argv,
int mode, char *what, int ret)
{
// 省略部分代码
// ContinueInNewThread是java.c中的方法
return ContinueInNewThread(ifn, threadStackSize, argc, argv, mode, what, ret);
}
2、jdk\src\share\bin\java.h中ContinueInNewThread0()函数声明。
/*
* Block current thread and continue execution in new thread
*/
int ContinueInNewThread0(int (JNICALL *continuation)(void *),
jlong stack_size, void * args);
3、jdk\src\share\bin\java.c中方法 ContinueInNewThread()核心代码。JavaMain函数的地址,在ContinueInNewThread0()函数中作为参数传入pthread_create()函数,这样在创建一个新的线程后,当新线程被执行时就会JavaMain函数逻辑。
#include "java.h"
// 省略部分代码
int ContinueInNewThread(InvocationFunctions* ifn, jlong threadStackSize,
int argc, char **argv,
int mode, char *what, int ret)
{
// 省略部分代码,仅仅保留核心代码
{
JavaMainArgs args;
int rslt;
args.argc = argc;
args.argv = argv;
args.mode = mode;
args.what = what;
args.ifn = *ifn;
// java.h中定义的方法ContinueInNewThread0(),该方法用于阻塞当前线程并创建新的线程执行java应用程序的启动过程
rslt = ContinueInNewThread0(JavaMain, threadStackSize, (void*)&args);
return (ret != 0) ? ret : rslt;
}
}
4、jdk\src\solaris\bin\java_md_solinux.c中方法ContinueInNewThread0()核心代码。
int ContinueInNewThread0(int (JNICALL *continuation)(void *), jlong stack_size, void * args) {
int rslt;
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)tmp;
}
// 省略部分代码
pthread_attr_destroy(&attr);
// 省略部分代码
return rslt;
}
更多推荐




所有评论(0)