揭秘JVM创世过程之紧急制动机制-异常处理

张开发
2026/6/30 4:46:49 15 分钟阅读
揭秘JVM创世过程之紧急制动机制-异常处理
前言本文旨在记录近期研读Java源码的学习心得与疑难问题。由于个人理解水平有限文中内容难免存在疏漏恳请读者不吝指正。Java世界的紧急制动机制在 OpenJDK 8u44 的源码中当 Java 初始化期间例如执行System.initializeSystemClass抛出异常时C 层感知并终止 VM 的过程并非通过 C 的try-catch机制而是通过一套基于“挂起异常字段Pending Exception”和“CHECK 宏”的显式检查机制。以下是这一过程的深度拆解1. 第一现场JavaThread的异常槽位在 JVM 内部每个线程JavaThread都有一个专门存放异常对象的指针字段。_pending_exception记录Java 层的异常对象oop_exception_file记录抛出异常的 C 文件_exception_line抛出异常的行号源码位置下面是整理后的示例代码。classThread:publicThreadShadow{// ...oop _pending_exception;// 指向 Java 层的异常对象oopconstchar*_exception_file;// 抛出异常的 C 文件int_exception_line;// 抛出异常的行号};hotspot\src\share\vm\runtime\thread.hpp原始代码classThread:publicThreadShadow{friendclassVMStructs;private:// Exception handling// (Note: _pending_exception and friends are in ThreadShadow)//oop _pending_exception; // pending exception for current thread// const char* _exception_file; // file information for exception (debugging only)// int _exception_line; // line information for exception (debugging only)// 省略部分代码}hotspot\src\share\vm\utilities\exceptions.hppclassThreadShadow:publicCHeapObjmtThread{friendclassVMStructs;protected:oop _pending_exception;// Thread has gc actions.constchar*_exception_file;// file information for exception (debugging only)int_exception_line;// line information for exception (debugging only)friendvoidcheck_ThreadShadow();// checks _pending_exception offset// 省略部分代码}当 Java 代码通过call_stub执行并抛出异常时Java 异常处理机制或者解释器/JIT 编译的代码会将异常对象的引用存入这个_pending_exception字段然后直接返回到 C。2. 感知机制万能的CHECK宏在Threads::create_vm等初始化代码中你会看到大量的 C 调用带有一个特殊的宏。这是 C 感知 Java 异常的“神经末梢”。源码位置src/share/vm/runtime/exceptions.hpp#defineCHECKTHREAD);if(HAS_PENDING_EXCEPTION)return;(void)(0#defineCHECK_0THREAD);if(HAS_PENDING_EXCEPTION)return0;(void)(0#defineCHECK_NHTHREAD);if(HAS_PENDING_EXCEPTION)returnHandle();(void)(0工作原理每当 C 层调用一个可能触发 Java 代码的函数如JavaCalls::call_static时紧接着就会执行if (HAS_PENDING_EXCEPTION)。这个宏会检查当前线程的_pending_exception是否为空。如果不为空说明 Java 层出事了C 函数会立即提前返回将异常向上传递。3. 拦截点Threads::create_vm中的关键检查在 VM 启动的核心函数Threads::create_vm中JVM 会分阶段检查初始化进度。如果核心类加载或初始化失败流程如下源码位置src/share/vm/runtime/thread.cppjintThreads::create_vm(JavaVMInitArgs*args,bool*canTryAgain){// ... 执行某项 Java 初始化任务 ...call_initialize_system_class(CHECK_0);// 如果这里抛出异常直接返回 0// 或者显式检查if(HAS_PENDING_EXCEPTION){// 发现异常进入收尾逻辑vm_exit_during_initialization(Handle(THREAD,PENDING_EXCEPTION));}}4. 物理栈的回溯穿过 Call Stub当异常发生且未在 Java 层被捕获时逻辑会回到Call Stub。现场还原Call Stub 执行其“收尾工作”根据之前保存的rbp和rsp还原 C 的寄存器环境。平滑着陆它并不处理异常逻辑它只是保证 CPU 能够安全地跳回到JavaCalls::call_helper里的下一条 C 指令。状态确认回到 C 后由于CHECK宏的存在当前的 C 函数会意识到“出事了”从而停止后续的初始化逻辑如停止加载其他类。5. 终结者vm_exit_during_initialization一旦 C 层确认在初始化这种关键时刻发生了异常它不会尝试恢复而是直接调用vm_exit_during_initialization。源码位置src/share/vm/runtime/java.cppvoidvm_exit_during_initialization(Handle exception){// 1. 打印异常堆栈让你看到那个著名的 Error occurred during initialization of VMjava_lang_Throwable::print_stack_trace(exception,tty);// 2. 终止 VMvm_abort(false);}这个函数会做两件事动作翻译异常调用java_lang_Throwable::print_stack_trace。虽然此时 Java 环境还没完全好但 JVM 已经有足够的“硬核”能力去解析那个异常对象的 backtrace并打印到 stderr。否则直接在 stderr 打印 C 侧记录的异常名。停掉所有线程通知安全点Safepoint机制尝试停止其他可能正在运行的辅助线程。销毁内存释放已经分配的部分本地资源。退出进程直接调用os::exit(1)或abort()不给 Java 留任何挣扎的机会强制关闭整个 JVM 进程。6. 为什么不直接用 C 的try-catch这是一个经典的架构选择真相性能与受控C 异常在跨模块尤其是在汇编生成的call_stub和 C 之间传递时开销极大且难以预测。隔离性Java 的异常是 Java 堆里的对象oop而 C 的异常是系统级的。JVM 需要精确控制“谁抛出了异常”以及“这个异常对象在 GC 扫描时是否存活”通过JavaThread字段来手动维护这种关系比依赖 C 运行时要稳健得多。总结在Java世界感知异常的真相是架构选择真相主动轮询性能与受控C 异常在跨模块尤其是在汇编生成的call_stub和 C 之间传递时开销极大且难以预测。隔离性Java 的异常是 Java 堆里的对象oop而 C 的异常是系统级的。JVM 需要精确控制“谁抛出了异常”以及“这个异常对象在 GC 扫描时是否存活”通过JavaThread字段来手动维护这种关系比依赖 C 运行时要稳健得多。Java 执行逻辑负责**“写”**异常标志_pending_exception。C 调用者负责在每一行关键代码后通过CHECK宏**“读”**异常标志。提前布局在进入 main 方法时的物理栈布局与异常感知当你进入main方法时物理栈就像一个精心布置的陷阱阵列Java 栈帧最顶端一旦抛出异常这里只负责填充异常信息。Call Stub 汇编层作为“时空隧道”它负责把 CPU 控制权从 Java 语义安全地交还给 C 语义。C 逻辑层底层通过CHECK宏像接力赛一样监控JavaThread-_pending_exception字段。从 Java 到 C 的感应链条Java 层发生异常将异常对象塞进JavaThread的_pending_exception。汇编层 (call_stub)清理 Java 栈帧跳回 C 调用处。C 层 (JavaCalls)函数返回通过HAS_PENDING_EXCEPTION宏感知到异常。VM 核心 (create_vm)发现异常在初始化关键路径上调用vm_exit_during_initialization。OS 层C 发起exit系统调用销毁整个物理进程。

更多文章