news 2026/5/16 5:59:15

揭秘JVM创世过程之Java线程栈真相

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
揭秘JVM创世过程之Java线程栈真相

前言

本文旨在记录近期研读Java源码的学习心得与疑难问题。由于个人理解水平有限,文中内容难免存在疏漏,恳请读者不吝指正。

Java 线程栈的“真相”

在 OpenJDK的实现中,Java 线程栈的“真相”可以用一句话概括:所谓的 Java 线程栈,物理上就是操作系统分配的 Native Stack,逻辑上被 JVM 划分成了不同的区域。
下面我们通过源码的执行路径,看看 JVM 是如何从操作系统“骗”来这块内存并将其改造为 Java 栈的。


1. 内存申请:os::create_thread阶段

当你调用Thread.start(),最终会进入平台相关的线程创建函数。在 Linux 上,这就是src/os/linux/vm/os_linux.cpp

核心源码片段 1:确定栈大小

JVM 首先要计算这个系统线程需要多大的栈。

// src/os/linux/vm/os_linux.cppboolos::create_thread(Thread*thread,ThreadType thr_type,size_t stack_size){...// 如果没有指定 -Xss,则根据线程类型获取默认栈大小if(stack_size<=0){stack_size=JavaThread::stack_size_at_create();}// 调整栈大小,确保包含 Guard Pages (警戒页)stack_size=MAX2(stack_size,os::Linux::min_stack_allowed);...pthread_attr_t attr;pthread_attr_init(&attr);// 关键:将 JVM 参数传递给 pthread 库pthread_attr_setstacksize(&attr,stack_size);

真相:此时,内存还没有真正分配。pthread_attr_setstacksize只是告诉内核:“待会儿给我创建线程时,请预留这么大的虚拟地址空间”。


2. 内存绑定:JavaThread对象的初始化

pthread_create成功后,子线程开始运行thread_native_entry。这时,JVM 需要把这块系统内存“锚定”到JavaThread对象上。

核心源码片段 2:记录栈边界
// src/share/vm/runtime/thread.cppvoidJavaThread::record_stack_base_and_size(){// 获取当前系统线程的栈底和大小address low_addr;size_t size;if(os::current_stack_size(&size,&low_addr)){// _stack_base 是高地址(栈生长的起点)_stack_base=low_addr+size;_stack_size=size;}}

真相:JavaThread并不拥有这块内存,它只是通过_stack_base_stack_size记录了这块系统分配内存的坐标。后续所有的 Java 栈帧压栈,本质上都在这块坐标范围内移动RSP寄存器。


3. 栈的逻辑分区:Guard Pages 的创建

为了防止 Java 代码递归太深导致系统崩溃,JVM 会在申请到的系统栈底部手动挖掘“陷阱”。

核心源码片段 3:设置警戒页
// src/share/vm/runtime/thread.cppvoidJavaThread::create_stack_guard_pages(){// 计算黄色警戒区和红色警戒区的位置// 通过 mprotect 系统调用,将对应的系统内存页设为“不可访问”if(os::guard_memory((address)low_addr,guard_size)){_stack_guard_state=stack_guard_enabled;}}

真相:这是 Java 栈和普通系统栈最大的不同。JVM 通过mprotect将栈底的几 KB 物理内存标记为禁止访问。

  • Yellow Zone:如果 CPU 访问到这里,说明栈快满了,JVM 捕获信号并抛出StackOverflowError
  • Red Zone:如果访问到这里,说明已经彻底失控,JVM 会直接打印Native Crash并强行退出。

4. 运行时真相:call_stub的压栈操作

当我们在本系列之前聊到的call_stub介入时,它如何处理这个栈?

核心源码片段 4:汇编层面的栈切换

src/cpu/x86/vm/stubGenerator_x86_64.cpp中:

__movptr(r13,entry_point);// 获取 Java 方法入口__mov(rbp,rsp);// 建立 C++ 栈帧基址// 接下来是关键:在当前系统栈上开辟 Java 区域__subptr(rsp,locals_size);// 移动 RSP,为 Java 局部变量腾出空间

真相:call_stub并没有切换到另一块内存,它只是在同一个pthread上,通过减小RSP(栈指针)的值,划出一块地盘给 Java 使用。


5. 总结:关于栈空间的三个终极真相

维度真相描述
分配本质Java 栈就是Native 内存。由mmap分配,不受 JVM GC 管理。
内存成本初始分配的是虚拟内存。只有当方法调用变深、触碰新页时,才会消耗物理内存 (RSS)
安全机制通过操作系统的页保护(Memory Protection)来实现StackOverflowError,而不是靠逻辑判断。
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/16 5:57:33

Z-Image-Turbo-辉夜巫女自动化运维实践:Linux常用命令与模型服务监控

Z-Image-Turbo-辉夜巫女自动化运维实践&#xff1a;Linux常用命令与模型服务监控 部署一个像Z-Image-Turbo-辉夜巫女这样的AI模型服务&#xff0c;只是第一步。真正考验人的&#xff0c;是把它放在服务器上之后&#xff0c;怎么让它稳定、高效地跑起来。模型服务不像静态网站&…

作者头像 李华
网站建设 2026/5/4 18:16:26

入门篇八 Nuxt4页面元信息与 SEO:让搜索引擎爱上你的网站

文章目录一、全局 meta 配置二、页面级 meta 配置三、动态 meta四、Open Graph 社交分享五、结构化数据&#xff08;JSON-LD&#xff09;六、规范链接&#xff08;Canonical URL&#xff09;七、robots.txt 和 sitemap八、性能优化 meta九、调试 SEO总结网站做得再漂亮&#xf…

作者头像 李华
网站建设 2026/4/12 14:48:35

MogFace人脸检测模型技术揭秘:CVPR2022论文复现+ResNet101骨干网络详解

MogFace人脸检测模型技术揭秘&#xff1a;CVPR2022论文复现ResNet101骨干网络详解 1. 引言&#xff1a;重新定义人脸检测的边界 想象一下这样的场景&#xff1a;你在整理家庭照片时&#xff0c;想要快速找出所有包含人脸的图片&#xff1b;或者作为开发者&#xff0c;需要为应…

作者头像 李华
网站建设 2026/4/14 3:27:01

小白程序员必看:Web安全入门指南,收藏学习,轻松进阶大模型!

小白程序员必看&#xff1a;Web安全入门指南&#xff0c;收藏学习&#xff0c;轻松进阶大模型&#xff01; 本文详细介绍了Web安全的基本概念、主要组成部分以及学习路径。从网络安全的重要性到Web安全的具体攻击手段&#xff0c;再到系统安全、数据安全等细分领域&#xff0c;…

作者头像 李华