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.mzph.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…