编译期通过静态遍历字节码指令流并模拟栈高变化来确定操作数栈最大深度:每条指令的压入/弹出量固定,分支取各路径峰值,循环按单次体分析,最终取所有可达路径最大值写入max_stack字段。

操作数栈深度和局部变量表大小不是运行时动态算出来的,而是在编译阶段由 javac 静态分析源码后确定,并直接写进 class 文件的 Code 属性里。
操作数栈最大深度怎么算出来的
javac 会模拟方法执行过程中表达式求值的最深状态。比如一个算术表达式 a + b * c,编译器会按字节码执行顺序推演:先压入 b 和 c(2个操作数),执行 imul 后弹出两个、压入 1 个结果;再压入 a,执行 iadd……整个过程操作数栈最多同时存几个值,就记为最大深度。
- 每条字节码指令对操作数栈的“压入”和“弹出”数量是固定的(如
iconst_1压入 1 个,istore_0弹出 1 个) - 编译器做一次线性扫描,跟踪栈高变化,取峰值
- long/double 占 2 个槽位,但只计为 1 个“操作数项”,深度仍按 entry 数累加
- 这个值最终存入
Code属性的max_stack字段
局部变量表大小怎么定的
局部变量表容量取决于方法签名参数个数 + 方法体内所有显式声明的局部变量 + 编译器隐式需要的变量(如 this 引用、某些异常处理结构中的临时变量)。
- 实例方法默认在索引 0 处存放
this(静态方法没有) - 每个基本类型或引用占 1 个 slot,long/double 占 2 个连续 slot
- 变量作用域不影响 slot 分配——只要声明过,slot 就被预留,哪怕后续代码不再使用
- 编译器遍历全部变量声明和参数,按 slot 编号规则累加,得出总 slot 数
- 该数值写入
Code属性的max_locals字段
为什么必须编译期确定
JVM 在执行 invokestatic 等调用指令时,要立刻为新栈帧分配固定内存空间。如果等运行时再计算,就无法保证栈帧布局一致,也无法做字节码验证(比如检查操作数栈是否溢出)。
- 栈帧结构一旦入栈,局部变量表大小和操作数栈容量就不能变
- 类加载阶段的验证器会检查
max_stack和max_locals是否与实际字节码操作匹配 - 这也是 Java 方法不能动态增减局部变量的根本原因











