当前位置: 首页 > article >正文

揭秘JVM创世过程之Call Stub进入Java世界的门票

前言本文旨在记录近期研读Java源码的学习心得与疑难问题。由于个人理解水平有限文中内容可能存在疏漏恳请读者不吝指正。前情回顾在揭秘JVM创世过程之两种语言首席外交官JavaCalls一文中将JVM看作Java世界中一个拥有两种语言的领事馆一边说C系统语另一边说Java字节码语。那么JavaCalls就是那个身着正装、手里拿着翻译机的首席外交官。当 JVM 需要从 C 内部逻辑如启动、反射、类初始化去执行一段 Java 代码时它必须通过JavaCalls。而JavaCalls最精彩的地方并不直接 jmp 到 Java 代码而是通过一个**“跳板” (Call Stub)**。JVM 在启动时是如何生成这段“跳板(Call Stub)”汇编代码进入Java世界的门票“跳板”代码Call Stub的生成过程是 JVM 启动时最硬核的底层操作之一。它不是由编译器提前编译好的而是 JVM 在运行初期直接在内存里**“现场手写”**出来的机器码。这个过程主要由StubGenerator和MacroAssembler这两个组件协作完成。1. 核心流程从 C 到机器码在Threads::create_vm执行过程中会调用init_globals()进而触发stubRoutines_init1()。这时JVM 会开启“机器码印刷机”。整个执行过程相关核心代码如下hotspot\src\share\vm\runtime\thread.cpp中Threads::create_vm()核心代码如下jintThreads::create_vm(JavaVMInitArgs*args,bool*canTryAgain){// 省略部分代码// Attach the main thread to this os threadJavaThread*main_threadnewJavaThread();main_thread-set_thread_state(_thread_in_vm);main_thread-record_stack_base_and_size();main_thread-initialize_thread_local_storage();main_thread-set_active_handles(JNIHandleBlock::allocate_block());// 省略部分代码// Initialize global modules// 在此方法中会执行stubRoutines_init1()完成“跳板”代码Call Stubjint statusinit_globals();if(status!JNI_OK){deletemain_thread;*canTryAgainfalse;// dont let caller call JNI_CreateJavaVM againreturnstatus;}// 省略部分代码{// The VM creates returns objects of this class. Make sure its initialized.initialize_class(vmSymbols::java_lang_Class(),CHECK_0);// The VM preresolves methods to these classes. Make sure that they get initializedinitialize_class(vmSymbols::java_lang_reflect_Method(),CHECK_0);initialize_class(vmSymbols::java_lang_ref_Finalizer(),CHECK_0);call_initializeSystemClass(CHECK_0);// 省略部分代码}// 省略部分代码}init_globals()方法核心代码hotspot\src\share\vm\runtime\init.cpp中init_globals()中调用stubRoutines_init1()和stubRoutines_init2()完成stubRoutines初始化。jintinit_globals(){// 省略部分代码bytecodes_init();classLoader_init();codeCache_init();VM_Version_init();os_init_globals();stubRoutines_init1();jint statusuniverse_init();// dependent on codeCache_init and// stubRoutines_init1 and metaspace_init.if(status!JNI_OK)returnstatus;javaClasses_init();// must happen after vtable initializationstubRoutines_init2();// note: StubRoutines need 2-phase init// 省略部分代码returnJNI_OK;}stubRoutines_init1()方法核心代码hotspot\src\share\vm\runtime\stubRoutines.cppvoidstubRoutines_init1(){StubRoutines::initialize1();}voidStubRoutines::initialize1(){if(_code1NULL){ResourceMark rm;TraceTimetimer(StubRoutines generation 1,TraceStartupTime);_code1BufferBlob::create(StubRoutines (1),code_size1);if(_code1NULL){vm_exit_out_of_memory(code_size1,OOM_MALLOC_ERROR,CodeCache: no room for StubRoutines (1));}CodeBufferbuffer(_code1);StubGenerator_generate(buffer,false);}}Step 1: 申请可执行内存 (Code Buffer)JVM 会在内存中开辟一块特殊的区域属于 CodeCache 的一部分并将其权限设置为可读、可写、可执行RWX。这块内存就是用来存放生成的汇编指令的“纸”。hotspot\src\share\vm\runtime\stubRoutines.cpp在方法initialize1()中通过BufferBlob::create()申请一块内存代码如下voidStubRoutines::initialize1(){if(_code1NULL){ResourceMark rm;TraceTimetimer(StubRoutines generation 1,TraceStartupTime);_code1BufferBlob::create(StubRoutines (1),code_size1);if(_code1NULL){vm_exit_out_of_memory(code_size1,OOM_MALLOC_ERROR,CodeCache: no room for StubRoutines (1));}CodeBufferbuffer(_code1);StubGenerator_generate(buffer,false);}}Step 2: 启动 MacroAssembler (宏汇编器)JVM 使用一个名为MacroAssembler的 C 类。这个类非常神奇它把每一条 CPU 指令如push,mov,call都封装成了一个 C 函数。当你调用masm-push(rax)时它并不是在执行 push而是在往刚才申请的内存缓冲区里写入0x50x86-64 架构下push rax的机器码。2. 生成 Call Stub 的具体代码逻辑在 OpenJDK 源码中以 x86_64 为例生成这段代码的逻辑位于src/hotspot/cpu/x86/stubGenerator_x86_64.cpp的generate_call_stub方法中。我们可以通过 C 源码窥见它“手写”汇编的过程addressgenerate_call_stub(addressreturn_address){// ... 准备缓冲区 ...StubCodeMarkmark(this,StubRoutines,call_stub);address start__pc();// 记录起始地址// 1. 保存调用者C的现场__enter();// push rbp; mov rbp, rsp__push(r15);// 保存关键寄存器__push(r14);__push(r13);__push(r12);// 2. 将 C 传进来的参数移动到指定的寄存器// 比如从 c_rarg1 (rsi) 拿到 Java 方法的入口地址__movptr(rbx,method);// 把 Method* 放入 rbx// 3. 构建 Java 栈帧// 这里会根据参数个数动态调整 rsp 的位置为 Java 参数腾地方// 4. 关键一跃跳入 Java 世界__call(entry_point);// 这里的 entry_point 就是解释器的入口// 5. 凯旋归来清理 Java 栈并恢复 C 寄存器__pop(r12);__pop(r13);__pop(r14);__pop(r15);__leave();__ret(0);// 返回到 JavaCalls::callreturnstart;// 返回这段代码在内存中的首地址}3. 如何变成函数指针生成完这段机器码后JVM 会做一个非常关键的动作记录地址将start这段代码的首地址存入全局变量StubRoutines::_call_stub_entry。强制转换在头文件中这个地址被定义为一个复杂的函数指针类型。调用当JavaCalls需要执行 Java 代码时它直接通过这个指针调用(*(CallStub)cast_to_fqn(_call_stub_entry))(...)这时CPU 就会从当前的 C 指令流直接跳转到那块刚才生成的内存区域开始执行那几条push和mov。4. 为什么要“现场手写”而不是写在.s汇编文件里你可能会问为什么不直接写个.s汇编文件编译进来动态性JVM 需要根据当前的硬件特性比如是否支持 AVX-512 指令集、是否开启了某些安全补丁来动态决定生成什么样的指令。极致优化现场生成可以根据当前的偏移量计算出最短的跳转指令Short Jump减少代码体积。统一抽象通过 C 宏汇编器JVM 可以用一套逻辑在不同的系统Linux/Windows/macOS上生成对应的机器码而不需要维护无数个版本的.s文件。总结JVM 生成跳板代码的过程就像是一个在施工现场直接烧制异形砖块的建筑师MacroAssembler是模具。内存缓冲区是原材料。StubGenerator是施工图纸。这种“运行时生成代码”的技术JIT 思想的萌芽确保了 Java 能够跨越 C 和字节码的鸿沟同时保持顶级的执行效率。

相关文章:

揭秘JVM创世过程之Call Stub进入Java世界的门票

前言 本文旨在记录近期研读Java源码的学习心得与疑难问题。由于个人理解水平有限,文中内容可能存在疏漏,恳请读者不吝指正。 前情回顾 在揭秘JVM创世过程之两种语言首席外交官JavaCalls,一文中将JVM看作Java世界中一个拥有两种语言的领事馆…...

告别教材下载烦恼:国家中小学智慧教育平台电子课本解析工具如何实现3分钟高效获取

告别教材下载烦恼:国家中小学智慧教育平台电子课本解析工具如何实现3分钟高效获取 【免费下载链接】tchMaterial-parser 国家中小学智慧教育平台 电子课本下载工具,帮助您从智慧教育平台中获取电子课本的 PDF 文件网址并进行下载,让您更方便地…...

CryptoJS不同加密模式对比:AES-CBC vs GCM在前端安全中的选择指南

AES加密模式深度解析:CBC与GCM在前端安全中的实战抉择 前端开发者在处理用户敏感数据时,AES加密已成为标配技术方案。但在具体实施过程中,加密模式的选择往往成为决策难点——是选择经典的CBC模式,还是拥抱更现代的GCM模式&#x…...

FreeRTOS实战:如何用TIM2定时器精准统计任务运行时间(附完整代码)

FreeRTOS任务性能调优实战:基于硬件定时器的精准统计与优化 在嵌入式系统开发中,任务执行时间的精确测量是性能调优的基础。想象一下,当你发现系统响应变慢时,如何快速定位哪个任务消耗了过多CPU资源?或者当系统出现偶…...

基于S7-300与组态王的智能药片装瓶机控制系统优化设计

1. 智能药片装瓶机控制系统的核心价值 在制药生产线上,药片装瓶环节看似简单却暗藏玄机。传统的人工装瓶方式不仅效率低下,还容易出现计数错误、交叉污染等问题。我曾在某药企亲眼见过工人因疲劳导致装瓶数量出错,最终整批药品不得不报废的案…...

51单片机实战:从零构建电子密码锁系统

1. 项目背景与硬件准备 第一次接触51单片机时,我就被它的实用性深深吸引。作为电子爱好者入门的最佳选择,STC89C52这款经典芯片就像乐高积木的基础模块——价格亲民(某宝20元就能买到开发板)、资源丰富(8K Flash、512…...

钢链数智,赋能实业——千匠网络钢铁产业电商系统,破解行业困局,激活钢铁增长新动能

钢铁行业作为国民经济的支柱产业,贯穿基建、制造、房地产、机械装备等核心领域,正处于从“规模扩张”向“质量提升”转型的关键阶段:从铁矿开采、冶炼轧制、钢材加工,到多级分销、终端采购、工程交付,全链路环节繁杂、…...

Pspice仿真新手避坑大全:为什么你的TL431仿真总报错?可能是模型库没加对

Pspice仿真新手避坑大全:为什么你的TL431仿真总报错? 刚接触Pspice的工程师们,是否经常遇到这样的场景:精心设计的TL431电路图明明检查了无数遍,点击仿真按钮后却弹出一堆令人困惑的错误提示?这就像拼好了乐…...

LangGraph多智能体框架:构建持久化AI智能体的终极指南 [特殊字符]

LangGraph多智能体框架:构建持久化AI智能体的终极指南 🚀 【免费下载链接】langgraph Build resilient language agents as graphs. 项目地址: https://gitcode.com/GitHub_Trending/la/langgraph 在当今快速发展的AI领域,多智能体框架…...

k8s中部署prometheus并监控k8s集群以及nginx案例

4台主机 node1主机:k8s集群中的master node2主机:搭建了harbor仓库,存储所需的docker镜像 test3、4主机:k8s集群中的woker 搭建prometheus https://github.com/prometheus-operator/kube-prometheus 获取prometheus压缩包的…...

美的集团2025年营收创新高、利润100%分红 落地1.3万个AI智能体

3月30日,美的集团发布2025年年报,实现营业总收入4585亿元,同比增长12.1%;归属于上市公司股东的净利润439.5亿元,同比上升14%。在业绩再创新高的同时,伴随我国“人工智能”行动的全面实施,美的集…...

PyTorch 2.8镜像真实效果:物理实验→电磁场/流体力学可视化视频

PyTorch 2.8镜像真实效果:物理实验→电磁场/流体力学可视化视频 1. 开箱即用的专业级物理模拟环境 当你第一次启动这个基于RTX 4090D优化的PyTorch 2.8镜像时,最直接的感受就是"专业工具就该这样"。这个镜像不是普通的深度学习环境&#xff…...

多场景适配:ClearerVoice-Studio支持16K/48K采样率,会议直播都适用

多场景适配:ClearerVoice-Studio支持16K/48K采样率,会议直播都适用 1. 为什么音频采样率如此重要? 在语音处理领域,采样率选择直接影响最终效果。就像相机像素决定照片清晰度一样,音频采样率决定了声音的"分辨率…...

VOOHU沃虎:从SFP到SFP28不同光模块如何选笼子?

在高速通信设备的设计中,SFP光模块笼子是一个看似简单却至关重要的组件。随着数据传输速率从1G演进到10G、25G乃至更高,光模块对笼子的要求也在发生质的变化。SFP(1G)、SFP(10G)、SFP28(25G&…...

5分钟上手Vane容器化部署:从零搭建隐私优先的AI搜索引擎

5分钟上手Vane容器化部署:从零搭建隐私优先的AI搜索引擎 【免费下载链接】Vane Vane is an AI-powered answering engine. 项目地址: https://gitcode.com/GitHub_Trending/pe/Vane 想要在5分钟内搭建一个功能强大的AI搜索引擎吗?Vane是一个专注于…...

Pixelorama:从像素小白到艺术大师的完整指南

Pixelorama:从像素小白到艺术大师的完整指南 【免费下载链接】Pixelorama Unleash your creativity with Pixelorama, a powerful and accessible open-source pixel art multitool. Whether you want to create sprites, tiles, animations, or just express yours…...

终极指南:如何让2012-2015年老款Mac安装最新macOS系统

终极指南:如何让2012-2015年老款Mac安装最新macOS系统 【免费下载链接】OpenCore-Legacy-Patcher Experience macOS just like before 项目地址: https://gitcode.com/GitHub_Trending/op/OpenCore-Legacy-Patcher 您的2012-2015年老款Mac是否已被苹果官方抛…...

聊天记录会消失?这款开源工具让数据永远属于你

聊天记录会消失?这款开源工具让数据永远属于你 【免费下载链接】WeChatMsg 提取微信聊天记录,将其导出成HTML、Word、CSV文档永久保存,对聊天记录进行分析生成年度聊天报告 项目地址: https://gitcode.com/GitHub_Trending/we/WeChatMsg …...

ModTheSpire模组加载器全攻略:解锁杀戮尖塔无限可能

ModTheSpire模组加载器全攻略:解锁杀戮尖塔无限可能 【免费下载链接】ModTheSpire External mod loader for Slay The Spire 项目地址: https://gitcode.com/gh_mirrors/mo/ModTheSpire 副标题:从零开始的模组探索之旅——让你的游戏体验突破边界…...

利用快马平台十分钟快速构建开源项目网站原型:以openclaw101为例

作为一个经常参与开源项目的开发者,我深知快速验证想法的重要性。最近在尝试为开源项目openclaw101搭建网站时,发现InsCode(快马)平台能完美解决从零搭建的繁琐过程。下面分享如何用十分钟完成一个具备完整功能的项目网站原型。 明确需求与功能规划 首先…...

落地生产级推理引擎!高性能GPU算子生成系统Kernel-Smith发布

在当今的大模型时代,高性能 GPU 算子(Kernel)是将硬件算力转化为实际吞吐量的核心引擎。无论是支撑 Megatron、vLLM、LMDeploy 等底层系统,还是驱动 AI for Science (AI4S) 的复杂科学计算,高效的算子实现都是释放硬件…...

效率飙升,跳过proteus安装配置,用快马ai秒建仿真项目

最近在做一个温度监测系统的项目,需要验证电路设计的可行性。按照传统方式,我得先下载安装Proteus软件,配置各种库文件,光是环境准备就得折腾半天。不过这次尝试了用InsCode(快马)平台的AI功能,整个过程变得异常高效。…...

探索GetQzonehistory:永久保存QQ空间记忆的数字时光机

探索GetQzonehistory:永久保存QQ空间记忆的数字时光机 【免费下载链接】GetQzonehistory 获取QQ空间发布的历史说说 项目地址: https://gitcode.com/GitHub_Trending/ge/GetQzonehistory 在数字时代,我们的记忆分散在各个社交平台,而Q…...

别再瞎调了!FOC电机控制中,采样电阻选型和PCB布局的5个实战避坑点

FOC电机控制实战指南:采样电阻选型与PCB布局的5个关键避坑点 在无刷电机控制领域,FOC(磁场定向控制)算法凭借其优异的动态性能和效率表现,已成为工业驱动、消费电子和机器人关节的主流方案。然而,许多工程师…...

基于Python的多媒体信息共享平台毕业设计源码

博主介绍:✌ 专注于Java,python,✌关注✌私信我✌具体的问题,我会尽力帮助你。一、研究目的本研究旨在设计并实现一个基于Python的多媒体信息共享平台,以满足现代网络环境下多媒体信息传播的需求。具体研究目的如下:构建一个高效、…...

基于GOOSE - Transformer - LSTM的数据回归预测探索

基于GOOSE-Transformer-LSTM的数据回归预测 模型结合Transformer的全局注意力机制和LSTM的短期记忆及序列处理能力 首先,采用Transformer自注意力机制捕捉数据的全局依赖性,并输出一个经过全局上下文编码的表示;然后,采用2024年最…...

ESP32-S3实战指南:SPI多设备管理与高效数据传输

1. ESP32-S3的SPI总线基础认知 第一次接触ESP32-S3的SPI总线时,我完全被各种专业术语搞懵了。后来在实际项目中反复折腾才发现,SPI本质上就是个"快递小哥",负责在芯片和外围设备之间搬运数据。ESP32-S3内置了4个这样的"快递站…...

若依框架实战:如何优雅地实现静态资源权限校验(附完整代码)

若依框架静态资源权限校验实战指南 在企业级应用开发中,静态资源的安全访问控制是一个常见需求。无论是小程序图片资源管理,还是企业内部文档权限控制,都需要确保只有授权用户才能访问特定资源。本文将深入探讨如何在若依(RuoYi)框架中实现静…...

快马AI助力:十分钟用Python搭建免费股票行情网站原型

最近想验证一个股票行情网站的原型,但作为独立开发者,从零搭建前后端实在太耗时。尝试用PythonFlask快速实现,结合InsCode(快马)平台的AI辅助功能,居然十分钟就完成了基础框架。记录下关键实现思路: 数据获取层设计 选…...

从YOLOv8到RTDETR:如何将训练后的YOLO指标无缝转换为COCO格式

1. 为什么需要YOLO到COCO格式转换 当你用YOLOv8官方代码训练RTDETR模型时,会发现评估结果默认输出的是YOLO格式指标。但学术界和工业界普遍采用COCO评估标准,这就好比在中国用人民币交易,到了欧洲就得换成欧元。我在去年帮某无人机公司做目标…...