1. 線程創建入口(JNI 層)
當 Java 層調用?Thread.start()
?時,JVM 通過 JNI 進入?JVM_StartThread
?函數:
JVM_ENTRY(void, JVM_StartThread(JNIEnv* env, jobject jthread))// 1. 檢查線程狀態,防止重復啟動if (java_lang_Thread::thread(jthread) != NULL) {throw_illegal_thread_state = true; // 已啟動則拋異常} else {// 2. 創建 JavaThread 對象,分配棧大小size_t sz = (size > 0) ? (size_t)size : 0;native_thread = new JavaThread(&thread_entry, sz); // 3. 綁定 Java 線程對象與 Native 線程native_thread->prepare(jthread);}// 4. 啟動線程Thread::start(native_thread); JVM_END
-
關鍵點:
-
通過?
JavaThread
?封裝線程信息,包括棧大小和入口函數?thread_entry
。 -
prepare()
?方法將 Java 層的?Thread
?對象與?JavaThread
?關聯。
-
2. 操作系統線程啟動(POSIX 線程)
通過?pthread_create
?創建操作系統線程:
int ret = pthread_create(&tid, &attr, thread_native_entry, thread);
-
入口函數?
thread_native_entry
:-
棧初始化:記錄棧基址和大小,隨機化棧空間(非 GLIBC 環境)以優化緩存。
-
線程狀態同步:使用?
Monitor
?通知父線程初始化完成,并等待喚醒。 -
執行主邏輯:調用?
thread->call_run()
,最終進入?JavaThread::run()
。
-
3. JVM 線程主邏輯(JavaThread::run)
void JavaThread::run() {initialize_tlab(); // 初始化線程本地分配緩沖區_stack_overflow_state.create_stack_guard_pages(); // 棧溢出保護ThreadStateTransition::transition(this, _thread_new, _thread_in_vm); // 狀態轉換set_active_handles(JNIHandleBlock::allocate_block()); // 分配 JNI 句柄塊thread_main_inner(); // 進入實際執行邏輯 }
-
關鍵操作:
-
TLAB 初始化:提升對象分配效率。
-
安全點同步:通過狀態轉換確保線程進入安全點。
-
調用?
thread_main_inner
:最終執行 Java 代碼。
-
4. 執行 Java 層 run() 方法
void JavaThread::thread_main_inner() {if (!has_pending_exception()) {this->entry_point()(this, this); // 調用 thread_entry} }static void thread_entry(JavaThread* thread, TRAPS) {JavaCalls::call_virtual(&result, obj, vmClasses::Thread_klass(),vmSymbols::run_method_name(), THREAD); }
-
最終跳轉:通過?
JavaCalls
?調用 Java?Thread
?對象的?run()
?方法,實現從 Native 到 Java 的過渡。
核心機制詳解
1. 棧隨機化(Cache Line 優化)
在非 GLIBC 系統(如 macOS)中,使用?alloca
?分配隨機大小棧空間:
void *stackmem = alloca(random); *(char *)stackmem = 1; // 防止編譯器優化
-
目的:避免不同線程的棧幀位于同一緩存行,減少緩存爭用(尤其超線程環境)。
2. 線程狀態同步
-
父子線程同步:通過?
Monitor
?和?wait/notify
?確保父線程等待子線程初始化完成。osthread->set_state(INITIALIZED); sync->notify_all(); while (osthread->get_state() == INITIALIZED) {sync->wait(); }
3. 信號處理
-
信號掩碼設置:
PosixSignals::hotspot_sigmask(thread)
?初始化線程信號掩碼,屏蔽某些信號(如 SIGSEGV)由 JVM 統一處理。
4. NUMA 支持
if (UseNUMA) {thread->set_lgrp_id(os::numa_get_group_id()); }
-
NUMA 優化:將線程綁定到就近內存節點,提升內存訪問效率。
5. 安全點(Safepoint)
-
狀態轉換:線程從?
_thread_new
?轉為?_thread_in_vm
,標志進入安全點區域,允許 JVM 進行垃圾回收等操作。 -
內存屏障:
OrderAccess::cross_modify_fence()
?確保指令順序,避免重排序問題。
總結:線程啟動流程
-
Java 層調用:
Thread.start()
?觸發 JNI 調用?JVM_StartThread
。 -
Native 線程創建:通過?
pthread_create
?創建 OS 線程,入口為?thread_native_entry
。 -
線程初始化:記錄棧信息、同步狀態、信號處理、NUMA 綁定。
-
執行 JVM 邏輯:調用?
JavaThread::run
,進入安全點,準備執行環境。 -
跳轉 Java 代碼:通過?
thread_entry
?調用 Java?run()
?方法,完成 Native 到 Java 的切換。
這一流程涵蓋了從操作系統線程創建到執行 Java 代碼的全鏈路,涉及 JVM 內部狀態管理、同步機制、性能優化及跨層級調用,是 JVM 線程模型的核心實現。
##源碼
// Thread start routine for all newly created threads
static void *thread_native_entry(Thread *thread) {thread->record_stack_base_and_size();#ifndef __GLIBC__// Try to randomize the cache line index of hot stack frames.// This helps when threads of the same stack traces evict each other's// cache lines. The threads can be either from the same JVM instance, or// from different JVM instances. The benefit is especially true for// processors with hyperthreading technology.// This code is not needed anymore in glibc because it has MULTI_PAGE_ALIASING// and we did not see any degradation in performance without `alloca()`.static int counter = 0;int pid = os::current_process_id();int random = ((pid ^ counter++) & 7) * 128;void *stackmem = alloca(random != 0 ? random : 1); // ensure we allocate > 0// Ensure the alloca result is used in a way that prevents the compiler from eliding it.*(char *)stackmem = 1;
#endifthread->initialize_thread_current();OSThread* osthread = thread->osthread();Monitor* sync = osthread->startThread_lock();osthread->set_thread_id(os::current_thread_id());if (UseNUMA) {int lgrp_id = os::numa_get_group_id();if (lgrp_id != -1) {thread->set_lgrp_id(lgrp_id);}}// initialize signal mask for this threadPosixSignals::hotspot_sigmask(thread);// initialize floating point control registeros::Linux::init_thread_fpu_state();// handshaking with parent thread{MutexLocker ml(sync, Mutex::_no_safepoint_check_flag);// notify parent threadosthread->set_state(INITIALIZED);sync->notify_all();// wait until os::start_thread()while (osthread->get_state() == INITIALIZED) {sync->wait_without_safepoint_check();}}log_info(os, thread)("Thread is alive (tid: " UINTX_FORMAT ", pthread id: " UINTX_FORMAT ").",os::current_thread_id(), (uintx) pthread_self());assert(osthread->pthread_id() != 0, "pthread_id was not set as expected");// call one more level start routinethread->call_run();// Note: at this point the thread object may already have deleted itself.// Prevent dereferencing it from here on out.thread = NULL;log_info(os, thread)("Thread finished (tid: " UINTX_FORMAT ", pthread id: " UINTX_FORMAT ").",os::current_thread_id(), (uintx) pthread_self());return 0;
}
int ret = pthread_create(&tid, &attr, (void* (*)(void*)) thread_native_entry, thread);// The main routine called by a new Java thread. This isn't overridden
// by subclasses, instead different subclasses define a different "entry_point"
// which defines the actual logic for that kind of thread.
void JavaThread::run() {// initialize thread-local alloc buffer related fieldsinitialize_tlab();_stack_overflow_state.create_stack_guard_pages();cache_global_variables();// Thread is now sufficiently initialized to be handled by the safepoint code as being// in the VM. Change thread state from _thread_new to _thread_in_vmThreadStateTransition::transition(this, _thread_new, _thread_in_vm);// Before a thread is on the threads list it is always safe, so after leaving the// _thread_new we should emit a instruction barrier. The distance to modified code// from here is probably far enough, but this is consistent and safe.OrderAccess::cross_modify_fence();assert(JavaThread::current() == this, "sanity check");assert(!Thread::current()->owns_locks(), "sanity check");DTRACE_THREAD_PROBE(start, this);// This operation might block. We call that after all safepoint checks for a new thread has// been completed.set_active_handles(JNIHandleBlock::allocate_block());if (JvmtiExport::should_post_thread_life()) {JvmtiExport::post_thread_start(this);}// We call another function to do the rest so we are sure that the stack addresses used// from there will be lower than the stack base just computed.thread_main_inner();
}void JavaThread::thread_main_inner() {assert(JavaThread::current() == this, "sanity check");assert(_threadObj.peek() != NULL, "just checking");// Execute thread entry point unless this thread has a pending exception// or has been stopped before starting.// Note: Due to JVM_StopThread we can have pending exceptions already!if (!this->has_pending_exception() &&!java_lang_Thread::is_stillborn(this->threadObj())) {{ResourceMark rm(this);this->set_native_thread_name(this->get_thread_name());}HandleMark hm(this);this->entry_point()(this, this);}DTRACE_THREAD_PROBE(stop, this);// Cleanup is handled in post_run()
}JVM_ENTRY(void, JVM_StartThread(JNIEnv* env, jobject jthread))JavaThread *native_thread = NULL;// We cannot hold the Threads_lock when we throw an exception,// due to rank ordering issues. Example: we might need to grab the// Heap_lock while we construct the exception.bool throw_illegal_thread_state = false;// We must release the Threads_lock before we can post a jvmti event// in Thread::start.{// Ensure that the C++ Thread and OSThread structures aren't freed before// we operate.MutexLocker mu(Threads_lock);// Since JDK 5 the java.lang.Thread threadStatus is used to prevent// re-starting an already started thread, so we should usually find// that the JavaThread is null. However for a JNI attached thread// there is a small window between the Thread object being created// (with its JavaThread set) and the update to its threadStatus, so we// have to check for thisif (java_lang_Thread::thread(JNIHandles::resolve_non_null(jthread)) != NULL) {throw_illegal_thread_state = true;} else {// We could also check the stillborn flag to see if this thread was already stopped, but// for historical reasons we let the thread detect that itself when it starts runningjlong size =java_lang_Thread::stackSize(JNIHandles::resolve_non_null(jthread));// Allocate the C++ Thread structure and create the native thread. The// stack size retrieved from java is 64-bit signed, but the constructor takes// size_t (an unsigned type), which may be 32 or 64-bit depending on the platform.// - Avoid truncating on 32-bit platforms if size is greater than UINT_MAX.// - Avoid passing negative values which would result in really large stacks.NOT_LP64(if (size > SIZE_MAX) size = SIZE_MAX;)size_t sz = size > 0 ? (size_t) size : 0;native_thread = new JavaThread(&thread_entry, sz);// At this point it may be possible that no osthread was created for the// JavaThread due to lack of memory. Check for this situation and throw// an exception if necessary. Eventually we may want to change this so// that we only grab the lock if the thread was created successfully -// then we can also do this check and throw the exception in the// JavaThread constructor.if (native_thread->osthread() != NULL) {// Note: the current thread is not being used within "prepare".native_thread->prepare(jthread);}}}if (throw_illegal_thread_state) {THROW(vmSymbols::java_lang_IllegalThreadStateException());}assert(native_thread != NULL, "Starting null thread?");if (native_thread->osthread() == NULL) {// No one should hold a reference to the 'native_thread'.native_thread->smr_delete();if (JvmtiExport::should_post_resource_exhausted()) {JvmtiExport::post_resource_exhausted(JVMTI_RESOURCE_EXHAUSTED_OOM_ERROR | JVMTI_RESOURCE_EXHAUSTED_THREADS,os::native_thread_creation_failed_msg());}THROW_MSG(vmSymbols::java_lang_OutOfMemoryError(),os::native_thread_creation_failed_msg());}#if INCLUDE_JFRif (Jfr::is_recording() && EventThreadStart::is_enabled() &&EventThreadStart::is_stacktrace_enabled()) {JfrThreadLocal* tl = native_thread->jfr_thread_local();// skip Thread.start() and Thread.start0()tl->set_cached_stack_trace_id(JfrStackTraceRepository::record(thread, 2));}
#endifThread::start(native_thread);JVM_END// In most of the JVM thread support functions we need to access the
// thread through a ThreadsListHandle to prevent it from exiting and
// being reclaimed while we try to operate on it. The exceptions to this
// rule are when operating on the current thread, or if the monitor of
// the target java.lang.Thread is locked at the Java level - in both
// cases the target cannot exit.static void thread_entry(JavaThread* thread, TRAPS) {HandleMark hm(THREAD);Handle obj(THREAD, thread->threadObj());JavaValue result(T_VOID);JavaCalls::call_virtual(&result,obj,vmClasses::Thread_klass(),vmSymbols::run_method_name(),vmSymbols::void_method_signature(),THREAD);
}