栈帧的局部变量表和操作数栈大小在编译期确定:max_locals统计最大并发slot数,max_stack记录执行中栈最高深度,均写入Code属性;动态链接索引与异常表等元数据也由javac静态生成。

栈帧的各个组成元素在编译期就已基本定型,不是运行时动态决定的。Java编译器(javac)在把源码编译为字节码时,会静态分析方法体,提前算出局部变量表和操作数栈所需的最大容量,并将这两个关键数值写入方法的 Code 属性 中。
局部变量表大小在编译期确定
编译器扫描整个方法体,统计所有显式声明的局部变量、方法参数(包括隐式的 this),并按类型分配槽位(slot):
- boolean、byte、char、short、int、float、reference、returnAddress 占 1 个 slot
- long 和 double 占连续 2 个 slot
- 未超出作用域的变量可能复用同一 slot,但总槽数取最大并发占用量
- 结果存入 Code 属性的 max_locals 字段,单位是 slot 数
操作数栈深度在编译期确定
编译器模拟字节码执行流程,追踪每条指令对栈的影响(如 iload 压栈、iadd 弹两值压一值),记录执行过程中栈达到的最高深度:
- 这个最大深度就是操作数栈所需空间,与运行时实际数据无关
- 结果存入 Code 属性的 max_stack 字段,单位是 operand stack slots
- 例如一个只做 int 加法的方法,max_stack 通常为 2(两个操作数)
动态链接与返回地址不占编译期“容量”,但依赖常量池索引
动态链接指向运行时常量池中的符号引用,方法返回地址由调用点决定——这两者本身不消耗栈帧的“内存配额”,但它们的定位信息在编译期就已固化:
- 每个方法调用指令(如 invokevirtual)都携带一个常量池索引,指向目标方法的符号引用
- 该索引在编译期生成,运行时由 JVM 解析为具体地址
- 方法返回地址虽不由编译器直接写入栈帧,但调用指令隐含的下一条指令位置已在字节码中固定
附加信息(如异常处理表)也在编译期生成
try-catch 结构会被编译器翻译为异常处理表(exception table),作为 Code 属性的一部分嵌入字节码:
- 每条表项包含 start_pc、end_pc、handler_pc、catch_type 四个字段
- 它不增加栈帧大小,但决定栈帧在异常发生时如何参与分派
- JVM 在方法执行前就加载并校验这张表,属于编译期产出的元数据

















