OpenJDK 17 中線程啟動的完整流程用C++ 源碼詳解

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()?確保指令順序,避免重排序問題。


總結:線程啟動流程

  1. Java 層調用Thread.start()?觸發 JNI 調用?JVM_StartThread

  2. Native 線程創建:通過?pthread_create?創建 OS 線程,入口為?thread_native_entry

  3. 線程初始化:記錄棧信息、同步狀態、信號處理、NUMA 綁定。

  4. 執行 JVM 邏輯:調用?JavaThread::run,進入安全點,準備執行環境。

  5. 跳轉 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);
}

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/80869.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/80869.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/80869.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Spring MVC參數傳遞

本內容采用最新SpringBoot3框架版本,視頻觀看地址:B站視頻播放 1. Postman基礎 Postman是一個接口測試工具,Postman相當于一個客戶端,可以模擬用戶發起的各類HTTP請求,將請求數據發送至服務端,獲取對應的響應結果。 2. Spring MVC相關注解 3. Spring MVC參數傳遞 Spri…

Python面向對象編程(OOP)深度解析:從封裝到繼承的多維度實踐

引言 面向對象編程(Object-Oriented Programming, OOP)是Python開發中的核心范式,其三大特性——??封裝、繼承、多態??——為構建模塊化、可維護的代碼提供了堅實基礎。本文將通過代碼實例與理論結合的方式,系統解析Python OOP的實現機制與高級特性…

0.66kV0.69kV接地電阻柜常規配置單

0.66kV/0.69kV接地電阻柜是變壓器中性點接地電阻柜中的特殊存在,主要應用于低壓柴油發電機組220V、火力發電廠380V、煤炭企業660V/690V等電力系統或電力用戶1000V的低壓系統中。 我們來看看0.66kV0.69kV接地電阻柜配置單: 配置特點如下: 1…

矩陣短劇系統:如何用1個后臺管理100+小程序?深度解析多端綁定技術

短劇行業效率革命!一套系統實現多平臺內容分發、數據統管與流量聚合 在短劇行業爆發式增長的今天,內容方和運營者面臨兩大核心痛點:多平臺運營成本高與流量分散難聚合。傳統模式下,每個小程序需獨立開發后臺,導致人力…

CSS可以繼承的樣式匯總

CSS可以繼承的樣式匯總 在CSS中,以下是一些常見的可繼承樣式屬性: 字體屬性:包括 font-family (字體系列)、 font-size (字體大小)、 font-weight (字體粗細)、 font-sty…

BFS算法篇——打開智慧之門,BFS算法在拓撲排序中的詩意探索(上)

文章目錄 引言一、拓撲排序的背景二、BFS算法解決拓撲排序三、應用場景四、代碼實現五、代碼解釋六、總結 引言 在這浩瀚如海的算法世界中,有一扇門,開啟后通向了有序的領域。它便是拓撲排序,這個問題的解決方法猶如一場深刻的哲學思考&#…

【Qt開發】信號與槽

目錄 1,信號與槽的介紹 2,信號與槽的運用 3,自定義信號 1,信號與槽的介紹 在Qt框架中,信號與槽機制是一種用于對象間通信的強大工具。它是在Qt中實現事件處理和回調函數的主要方法。 信號:窗口中&#x…

數據庫基礎:概念、原理與實戰示例

在當今信息時代,數據已經成為企業和個人的核心資產。無論是社交媒體、電子商務、金融交易,還是物聯網設備,幾乎所有的現代應用都依賴于高效的數據存儲和管理。數據庫(Database)作為數據管理的核心技術,幫助…

前端-HTML基本概念

目錄 什么是HTML 常用的瀏覽器引擎是什么? 常見的HTML實體字符 HTML注釋 HTML語義化是什么?為什么要語義化?一定要語義化嗎? 連續空格如何渲染? 聲明文檔類型 哪些字符集編碼支持簡體中文? 如何解…

Linux進程信號處理(26)

文章目錄 前言一、信號的處理時機處理情況“合適”的時機 二、用戶態與內核態概念重談進程地址空間信號的處理過程 三、信號的捕捉內核如何實現信號的捕捉?sigaction 四、信號部分小結五、可重入函數六、volatile七、SIGCHLD 信號總結 前言 這篇就是我們關于信號的最…

C++ 字符格式化輸出

文章目錄 一、簡介二、實現代碼三、實現效果 一、簡介 這里使用std標準庫簡單實現一個字符格式化輸出&#xff0c;方便后續的使用&#xff0c;它有點類似Qt中的QString操作。 二、實現代碼 FMTString.hpp #pragma once#include <cmath> #include <cstdio> #include…

python高級特性

json.dumps({a:1,n:2}) #Python 字典類型轉換為 JSON 對象。相當于jsonify data2 json.loads(json_str)#將 JSON 對象轉換為 Python 字典 異步編程&#xff1a;在異步編程中&#xff0c;程序可以啟動一個長時間運行的任務&#xff0c;然后繼續執行其他任務&#xff0c;而無需等…

ubuntu24離線安裝docker

一、確認ubuntu版本 root@dockerserver:/etc/pam.d# lsb_release -a No LSB modules are available. Distributor ID: Ubuntu Description: Ubuntu 24.04.2 LTS Release: 24.04 Codename: noble 根據codename確認。 docker官方網址下載 https://download.docker.com/linux/…

索尼(sony)攝像機格式化后mp4的恢復方法

索尼(sony)的Alpha 7 Ⅳ系列絕對稱的上是索尼的“全畫幅標桿機型”&#xff0c;A7M4配備了3300萬像素的CMOS&#xff0c;以及全新研發的全畫幅背照式Exmor R?CMOS影像傳感器&#xff0c;搭載BIONZ XR?影像處理器&#xff0c;與旗艦微單?Alpha 1如出一轍。下面我們來看看A7M4…

2025最新出版 Microsoft Project由入門到精通(七)

目錄 優化資源——在資源使用狀況視圖中查看資源的負荷情況 在資源圖表中查看資源的負荷情況 優化資源——資源出現沖突時的原因及處理辦法 資源過度分類的處理解決辦法 首先檢查任務工時的合理性并調整 增加資源供給 回到資源工作表中雙擊對應的過度分配資源 替換資…

最短路與拓撲(1)

1、找最長良序字符串 #include<bits/stdc.h> using namespace std; const int N105; int dis[N]; int vis[N]; int edge[N][N]; int n,m; int vnum;void dij(int u, int v) {// 初始化距離數組和訪問標記for(int i0; i<vnum; i) {vis[i] 0;dis[i] edge[u][i];}// D…

降低60.6%碰撞率!復旦大學地平線CorDriver:首次引入「走廊」增強端到端自動駕駛安全性

導讀 復旦大學&地平線新作-CorDriver: 首次通過引入"走廊"作為中間表征&#xff0c;揭開一個新的范式。預測的走廊作為約束條件整合到軌跡優化過程中。通過擴展優化的可微分性&#xff0c;使優化后的軌跡能無縫地在端到端學習框架中訓練&#xff0c;從而提高安全…

CSS flex:1

在 CSS 中&#xff0c;flex: 1 是一個用于彈性布局&#xff08;Flexbox&#xff09;的簡寫屬性&#xff0c;主要用于控制 flex 項目&#xff08;子元素&#xff09;如何分配父容器的剩余空間。以下是其核心作用和用法&#xff1a; 核心作用 等分剩余空間&#xff1a;讓 flex …

1.6 關于static和final的修飾符

一.static static是靜態修飾符&#xff0c;用于修飾類成員&#xff08;變量&#xff0c;方法&#xff0c;代碼塊&#xff09; 被修飾的類成員屬于類&#xff0c;不必生成示例&#xff0c;即可直接調用屬性或者方法。 關于代碼塊&#xff0c;被static修飾的代碼塊是靜態代碼塊…

數據結構—(鏈表,棧,隊列,樹)

本文章寫的比較亂&#xff0c;屬于是縫合怪&#xff0c;很多細節沒處理&#xff0c;顯得粗糙&#xff0c;日后完善&#xff0c;今天趕時間了。 1. 紅黑樹的修復篇章 2. 紅黑樹的代碼理解&#xff08;部分寫道注釋之中了&#xff09; 3. 隊列與棧的代碼 4. 重要是理解物理邏輯&a…