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
调用 JavaThread
对象的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
调用 Javarun()
方法,完成 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);
}