详解JVM中的栈帧结构_局部变量表、操作数栈与动态链接的作用

星敏小哥_9292

星敏小哥_9292

2026-02-15

999人浏览

原创

局部变量表存储编译期分配索引位置的变量值,long和double占两个槽因其8字节且需连续槽位对齐;操作数栈采用出栈入栈模型以匹配表达式求值顺序并优化性能;动态链接通过类加载时解析符号引用或运行时invokedynamic绑定实现方法目标定位。

详解jvm中的栈帧结构_局部变量表、操作数栈与动态链接的作用

局部变量表存的是什么,为什么long和double占两个槽

局部变量表不是按“变量名”存数据,而是按“编译期分配的索引位置”存值。每个槽(slot)固定 4 字节,int、float、reference、returnAddress各占 1 槽;long 和 double 占 2 槽,因为它们是 8 字节,且 JVM 要求连续槽位对齐。

常见错误现象:VerifyError: Operand stack overflow 或方法调用后读到错误值,往往是因为手动计算 slot 索引时没跳过 long/double 占用的第二个槽,导致后续变量写入错位。

  • 实例方法的局部变量表第 0 号槽固定存 this 引用(静态方法没有)
  • 编译器可能复用槽位:比如 int a = 1; {...} int b = 2; 中 a 和 b 可能共用同一槽
  • Java 8+ 的 -g:vars 编译选项才能在调试信息里看到变量名与槽号的映射,否则仅靠字节码无法反推原始变量名

操作数栈为什么是“出栈入栈”模型,而不是数组下标访问

操作数栈本质是为字节码指令服务的临时工作区,设计成栈结构是为了匹配绝大多数表达式求值顺序(如 iadd 总是从栈顶弹出两个操作数),避免引入寄存器编号或复杂寻址逻辑,简化解释器实现。

性能影响很实际:栈顶缓存(TosCache)是 HotSpot 的关键优化,它把栈顶 1–2 个元素缓存在 CPU 寄存器里。一旦操作数栈深度超过缓存容量,就会触发内存读写,性能明显下降。

  • 每次方法调用前,JVM 根据 Code 属性中的 max_stack 值预分配栈空间,这个值由编译器静态分析得出,不是运行时伸缩的
  • dup、swap 这类指令只操作栈顶有限几个元素,不会遍历整个栈 —— 所以它不是“栈”在数据结构意义上,而是“栈式访问协议”
  • 如果手写字节码或用 ASM 修改,max_stack 算小了,运行时抛 StackOverflowError(注意:不是 Java 层的 StackOverflowError,而是 JVM 启动时报错)

动态链接怎么实现“同一个方法调用,运行时指向不同实际目标”

动态链接不是在调用时才去查符号表,而是在类加载阶段就把符号引用(如 invokevirtual java/io/PrintStream.println:(I)V)解析成具体的方法入口地址,并存进运行时常量池的对应项中。但“解析时机”分两类:invokedynamic 是真·运行时绑定,其余指令在类初始化前就完成解析(前提是目标方法不被子类重写)。

Adobe Podcast
Adobe Podcast

Adobe Podcast是一款AI音频处理工具,Adobe推出的在线AI音频录制和编辑工具。

下载

容易踩的坑:当父类方法被子类重写,又用了 invokespecial(比如构造器、私有方法、super.xxx()),JVM 就绕过虚方法表,直接跳转到声明类型的方法体 —— 这就是为什么在构造器里调用可重写方法会出问题。

  • invokestatic 和 invokespecial 绑定目标在解析阶段就确定,属于“静态分派”
  • invokevirtual 和 invokeinterface 依赖对象实际类型 + 方法表(vtable / itable),是“动态分派”,但查找过程本身很快(现代 JVM 有内联缓存)
  • invokedynamic 的引导方法(bootstrap method)由用户代码控制,Lambda 表达式、字符串拼接等都靠它,首次调用慢,之后基本和普通调用一样快

局部变量表、操作数栈、动态链接三者如何配合完成一次方法调用

以 obj.foo(a, b) 为例:先将 obj 压入操作数栈(从局部变量表第 n 号槽取),再压入 a、b;执行 invokevirtual 时,JVM 查 obj 的实际类的 vtable,找到 foo 入口;进入新栈帧后,局部变量表第 0 槽存 this(即 obj),第 1、2 槽依次存 a、b;方法体内所有对参数的读写,都通过局部变量表索引完成,中间计算全走操作数栈。

关键细节常被忽略:局部变量表和操作数栈的数据不能跨栈帧共享。即使两个栈帧里都有同名变量,它们物理上完全隔离 —— 这也是为什么递归不会污染上层变量,但栈空间会耗尽。

  • 局部变量表负责“命名存储”,操作数栈负责“临时运算”,动态链接负责“目标定位”,三者分工明确,缺一不可
  • 逃逸分析可能让局部变量表里的对象直接分配在栈上(栈上分配),但这不影响局部变量表本身的结构和访问方式
  • GC Roots 包含当前栈帧的局部变量表中的 reference,所以只要变量还在表里(哪怕代码逻辑已不再使用),对象就不会被回收

相关文章

PHP速学视频免费教程(入门到精通)
PHP速学视频免费教程(入门到精通)

PHP怎么学习?PHP怎么入门?PHP在哪学?PHP怎么学才快?不用担心,这里为大家提供了PHP速学教程(入门到精通),有需要的小伙伴保存下载就能学习啦!

下载

相关标签:

本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

相关专题

更多
bootstrap框架介绍
bootstrap框架介绍

本专题整合了bootstrap框架相关介绍,阅读专题下面的文章了解更多详细内容。

2026.03.18

745

21

bootstrap安装教程
bootstrap安装教程

本专题整合了bootstrap安装相关教程,阅读专题下面的文章了解更多详细操作教程。

2026.03.18

199

9

css中float用法
css中float用法

css中float属性允许元素脱离文档流并沿其父元素边缘排列,用于创建并排列、对齐文本图像、浮动菜单边栏和重叠元素。想了解更多float的相关内容,可以阅读本专题下面的文章。

2024.04.28

7107

8

C++中int、float和double的区别
C++中int、float和double的区别

本专题整合了c++中int和double的区别,阅读专题下面的文章了解更多详细内容。

2025.10.23

824

4

java基础知识汇总
java基础知识汇总

java基础知识有Java的历史和特点、Java的开发环境、Java的基本数据类型、变量和常量、运算符和表达式、控制语句、数组和字符串等等知识点。想要知道更多关于java基础知识的朋友,请阅读本专题下面的的有关文章,欢迎大家来php中文网学习。

2023.10.24

6224

49

java基础知识汇总
java基础知识汇总

java基础知识有Java的历史和特点、Java的开发环境、Java的基本数据类型、变量和常量、运算符和表达式、控制语句、数组和字符串等等知识点。想要知道更多关于java基础知识的朋友,请阅读本专题下面的的有关文章,欢迎大家来php中文网学习。

2023.10.24

6224

49

java基础知识汇总
java基础知识汇总

java基础知识有Java的历史和特点、Java的开发环境、Java的基本数据类型、变量和常量、运算符和表达式、控制语句、数组和字符串等等知识点。想要知道更多关于java基础知识的朋友,请阅读本专题下面的的有关文章,欢迎大家来php中文网学习。

2023.10.24

6224

49

js 字符串转数组
js 字符串转数组

js字符串转数组的方法:1、使用“split()”方法;2、使用“Array.from()”方法;3、使用for循环遍历;4、使用“Array.split()”方法。本专题为大家提供js字符串转数组的相关的文章、下载、课程内容,供大家免费下载体验。

2023.08.03

1838

5

js截取字符串的方法
js截取字符串的方法

js截取字符串的方法有substring()方法、substr()方法、slice()方法、split()方法和slice()方法。本专题为大家提供字符串相关的文章、下载、课程内容,供大家免费下载体验。

2023.09.04

2924

5

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
GDB Reference Card
GDB Reference Card

共0课时 | 0人学习

《Debugging with GDB》用户手册
《Debugging with GDB》用户手册

共0课时 | 0人学习

Valgrind FAQ
Valgrind FAQ

共0课时 | 0人学习