专家视角看JVM_StartThread

前言

本文旨在记录近期研读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对象。

    cpp 复制代码
    JavaThread* 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定义

    cpp 复制代码
    class 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 的构造函数会触发一系列连锁反应:

    1. 初始化 OSThread:申请 OS 级别的线程句柄。
    2. 调用 os::create_thread :这是跨平台的转折点。在 Linux 下,它会在这里计算栈大小并最终调用 pthread_create
    3. 特别说明
      这里的 new 触发的是 HotSpot 重写过的 operator new。它不仅仅是分配内存,还会初始化 JavaThread 内部的一系列复杂结构:
      • OSThread :持有平台相关的线程句柄(如 pthread_t)。
      • ParkEvent & Parker :用于实现 LockSupport.park() 的底层同步原语。
      • JNIEnv:为该线程分配专属的 JNI 环境指针。
  • stack_size 的传递 :注意变量 sz。如果你在 Java 构造函数里传入了 stackSize,它会在这里被传递给 pthread_attr_setstacksize

  • 核心代码

    1. hotspot\src\share\vm\runtime\thread.cpp源码中JavaThread::JavaThread()核心逻辑。
    cpp 复制代码
    JavaThread::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);
    
    }
    1. hotspot\src\os\linux\vm\os_linux.cpp源码中os::create_thread()核心逻辑
    cpp 复制代码
    bool 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 创建后的初始阶段)。主线程需要完成最后的手续。

核心步骤:
  1. 分配编译器资源 :为新线程分配 CompileBroker 相关的缓冲区。
  2. 准备 JNIEnv:为新线程初始化 JNI 环境。
  3. 加入名单 :将 native_thread 指针存入 Java 对象的 eetop 字段,并将线程加入 JVM 的全局 Threads 列表。
  4. 点火(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。

相关推荐
极客on之路1 小时前
线上 JVM 出问题
运维·服务器·jvm
敲上瘾1 小时前
高并发内存池(三):PageCache(页缓存)的实现
linux·c++·缓存·高并发内存池·池化技术
awljwlj2 小时前
黑马点评复习—缓存相关【包含可能的问题和基础知识复习】
java·后端·spring·缓存
Gofarlic_OMS2 小时前
ENOVIA基于Token的许可证消费模式分析与分点策略
java·大数据·开发语言·人工智能·制造
2401_865439632 小时前
HTML5中SVG原生动画标签Animate的基础用法
jvm·数据库·python
ROLL.72 小时前
Git和Repo
java·git·安卓
Ops菜鸟(Xu JieHao)2 小时前
Linux 内网远程桌面Xrdp ~保姆级教程
linux·运维·服务器·windows·远程桌面·远程·xrdp
zjeweler2 小时前
linux服务器部署openclaw最新最细教程(非docker版)
linux·服务器·docker·openclaw
minglie12 小时前
zynq裸机和linux spidev操作W25Q16
linux