前言
本文旨在记录近期研读Java源码的学习心得与疑难问题。由于个人理解水平有限,文中内容难免存在疏漏,恳请读者不吝指正。
JVM_StartThread源码拆解
JVM_StartThread 是连接 Java 虚拟世界与 C++ 物理实体的关键纽带。它定义在 hotspot/src/share/vm/prims/jvm.cpp 中,作为 JNI 方法 Thread.start0() 的内核实现。
在 OpenJDK 8的源码世界里,JVM_StartThread 是线程从"Java 堆中的一个普通对象"蜕变为"内核调度实体"的移民局 。它位于 hotspot/src/share/vm/prims/jvm.cpp,承担了所有繁重的准入校验和资源申请工作。
我们从专家专家视角,直接切入核心源码,拆解这台"线程发动机"的三个核心模块。
1. 准入检查:拒绝"二次投胎"
虽然在 Java 层面,Thread.start() 已经通过 threadStatus 做了初步检查。但 JVM 并不信任 Java 层,它在 C++ 层实施了物理级的准入控制。
hotspot\src\share\vm\prims\jvm.cpp源码中JVM_StartThread()核心逻辑。
cpp
// 源码位置:jvm.cpp
JVM_ENTRY(void, JVM_StartThread(JNIEnv* env, jobject jthread))
// 省略部分代码,只保留核心代码
JavaThread *native_thread = NULL;
bool throw_illegal_thread_state = false;
{
// 1. 核心锁:Threads_lock 保护全局线程列表
MutexLocker mu(Threads_lock);
// 2. 物理检查:检查 Java 对象中的 eetop 字段
// 如果 eetop 指向的 C++ JavaThread 对象已存在,说明线程已经启动过
if (java_lang_Thread::thread(JNIHandles::resolve_non_null(jthread)) != NULL) {
throw_illegal_thread_state = true;
} else {
// ... 申请资源逻辑
}
}
JVM_END
-
eetop 屏障 :JVM 通过
java_lang_Thread::thread检查该 Java 对象是否已经绑定了底层的JavaThread。这是防止IllegalThreadStateException的最后一道防线。核心逻辑 :
hotspot\src\share\vm\classfile\javaClasses.cpp源码中java_lang_Thread::thread()方法可以获取Java对象绑定底层的JavaThread对象。cppJavaThread* java_lang_Thread::thread(oop java_thread) { return (JavaThread*)java_thread->address_field(_eetop_offset); }hotspot\src\share\vm\classfile\javaClasses.hpp源码中java_lang_Thread类中_eetop_offset定义cppclass java_lang_Thread : AllStatic { private: // 省略部分代码 static int _eetop_offset; // 省略部分代码 } -
全局互斥 :整个检查过程在
Threads_lock的保护下进行,确保在并发场景下,同一个Thread对象不会被两个父线程同时"点火"。
2. 申请 C++ 对象:从堆内存到系统资源的跨越
如果准入通过,JVM 开始在 Native Memory(非 Java 堆)中为新线程构筑"肉身"。
hotspot\src\share\vm\prims\jvm.cpphotspot\src\share\vm\prims\jvm.cpp源码中JVM_ENTRY(void, JVM_StartThread(JNIEnv* env, jobject jthread))申请JavaThread对象的主要逻辑。
cpp
// 源码逻辑:申请 JavaThread 对象
jlong size = java_lang_Thread::stackSize(JNIHandles::resolve_non_null(jthread));
size_t sz = size > 0 ? (size_t)size : 0;
// 这里是真正的 C++ 对象申请
native_thread = new JavaThread(&thread_entry, sz);
深度解析:
-
new JavaThread:这不仅仅是内存分配。在 C++ 层面,JavaThread的构造函数会触发一系列连锁反应:- 初始化
OSThread:申请 OS 级别的线程句柄。 - 调用
os::create_thread:这是跨平台的转折点。在 Linux 下,它会在这里计算栈大小并最终调用pthread_create。 - 特别说明
这里的new触发的是 HotSpot 重写过的operator new。它不仅仅是分配内存,还会初始化JavaThread内部的一系列复杂结构:OSThread:持有平台相关的线程句柄(如pthread_t)。ParkEvent&Parker:用于实现LockSupport.park()的底层同步原语。JNIEnv:为该线程分配专属的 JNI 环境指针。
- 初始化
-
stack_size的传递 :注意变量sz。如果你在 Java 构造函数里传入了stackSize,它会在这里被传递给pthread_attr_setstacksize。 -
核心代码:
hotspot\src\share\vm\runtime\thread.cpp源码中JavaThread::JavaThread()核心逻辑。
cppJavaThread::JavaThread(ThreadFunction entry_point, size_t stack_sz) : Thread() #if INCLUDE_ALL_GCS , _satb_mark_queue(&_satb_mark_queue_set), _dirty_card_queue(&_dirty_card_queue_set) #endif // INCLUDE_ALL_GCS { initialize(); _jni_attach_state = _not_attaching_via_jni; set_entry_point(entry_point); // Create the native thread itself. // %note runtime_23 os::ThreadType thr_type = os::java_thread; thr_type = entry_point == &compiler_thread_entry ? os::compiler_thread : os::java_thread; os::create_thread(this, thr_type, stack_sz); }hotspot\src\os\linux\vm\os_linux.cpp源码中os::create_thread()核心逻辑
cppbool os::create_thread(Thread* thread, ThreadType thr_type, size_t stack_size) { assert(thread->osthread() == NULL, "caller responsible"); // Allocate the OSThread object OSThread* osthread = new OSThread(NULL, NULL); if (osthread == NULL) { return false; } // set the correct thread state osthread->set_thread_type(thr_type); // Initial state is ALLOCATED but not INITIALIZED osthread->set_state(ALLOCATED); thread->set_osthread(osthread); // 省略部分代码 if (os::Linux::supports_variable_stack_size()) { // calculate stack size if it's not specified by caller if (stack_size == 0) { stack_size = os::Linux::default_stack_size(thr_type); switch (thr_type) { case os::java_thread: // Java threads use ThreadStackSize which default value can be // changed with the flag -Xss assert (JavaThread::stack_size_at_create() > 0, "this should be set"); stack_size = JavaThread::stack_size_at_create(); break; // 省略部分代码 } } stack_size = MAX2(stack_size, os::Linux::min_stack_allowed); pthread_attr_setstacksize(&attr, stack_size); } else { // let pthread_create() pick the default value. } }
3. 总控逻辑:握手与点火
即便 C++ 对象创建成功,新线程此时仍处于"冻结"状态(处于 pthread_create 创建后的初始阶段)。主线程需要完成最后的手续。
核心步骤:
- 分配编译器资源 :为新线程分配
CompileBroker相关的缓冲区。 - 准备
JNIEnv:为新线程初始化 JNI 环境。 - 加入名单 :将
native_thread指针存入 Java 对象的eetop字段,并将线程加入 JVM 的全局Threads列表。 - 点火(Prepare & Start):
cpp
// 源码逻辑
if (native_thread->osthread() != NULL) {
// 预备:设置线程状态为 _thread_in_vm
Thread::start(native_thread);
}
核心逻辑
hotspot\src\share\vm\runtime\thread.cpp
cpp
void JavaThread::prepare(jobject jni_thread, ThreadPriority prio) {
Handle thread_oop(Thread::current(), JNIHandles::resolve_non_null(jni_thread));
set_threadObj(thread_oop());
// 设置eetop字段
java_lang_Thread::set_thread(thread_oop(), this);
if (prio == NoPriority) {
prio = java_lang_Thread::priority(thread_oop());
assert(prio != NoPriority, "A valid priority should be present");
}
// Push the Java priority down to the native thread; needs Threads_lock
Thread::set_priority(this, prio);
// 什么都没有做,可以忽略
prepare_ext();
// 将新的的线程添加到全局Threads列表
Threads::add(this);
}
hotspot\src\share\vm\classfile\javaClasses.cpp源码中java_lang_Thread::set_thread()源码如下:
cpp
void java_lang_Thread::set_thread(oop java_thread, JavaThread* thread) {
java_thread->address_field_put(_eetop_offset, (address)thread);
}
关键真相:
Thread::start(native_thread) 内部会释放一个信号量/锁 (在 Linux 下通常是 sync->release())。此时,那个在 os::create_thread 中创建并一直处于阻塞等待状态的新线程,终于接收到了"绿灯",开始执行它的 thread_native_entry。
专家视点:JVM_StartThread 的"原子性"错觉
虽然 JVM_StartThread 看起来是一个整体,但它其实是父线程在忙活。
- 父线程 :负责
new JavaThread、修改全局列表、释放启动信号。 - 子线程 :在内核里诞生后一直处于"待命"状态,直到父线程完成
Thread::start。
资深陷阱 :如果在
new JavaThread成功后但Thread::start执行前,JVM 发生了 OOM 或 Crash,你会发现一个"幽灵线程"------OS 层面已经有了 LWP,但 Java 层面的isAlive()却永远返回 false。