局部变量表的slot按作用域生命周期动态复用,而非声明顺序;编译期确定编号,运行时直接读写,long/double占两个连续slot,localvariabletable仅为调试信息,不参与执行。

局部变量表的slot到底按什么分配?
不是按声明顺序挨个占位,而是按作用域生命周期动态复用。一个slot可以先后被不同局部变量使用,只要它们的作用域不重叠。
比如int a = 1;在if块里声明,String b = "x";在另一个if块里,它们很可能共用同一个slot——JVM不会为每个变量预留独立位置。
- 编译期就确定
slot编号,运行时只写入/读取,不校验“当前该不该用这个slot” - 方法参数从
slot 0开始:静态方法是0,1,2...,实例方法0是this,之后才是显式参数 -
long和double占两个连续slot(如slot 2和slot 3),其余类型都只占一个
为什么javap -v看到的LocalVariableTable属性和实际slot对不上?
LocalVariableTable是调试信息,只记录变量名、类型、作用域起止的字节码偏移(start和length),它不决定slot分配,也不影响执行。即使删掉这个属性(用-g:none编译),程序照样跑,只是IDE里看不到变量名。
- 同一个
slot在不同字节码区间可能对应不同变量名,LocalVariableTable会列出多条记录 - 没加
-g编译时,LocalVariableTable为空,但slot依然存在且被指令访问 - 反编译工具(如JD-GUI)依赖这个表还原变量名,但它不是JVM执行的依据
slot复用导致的典型问题:Lambda捕获变量时“变量已改变”
不是Java语言层面的错误,而是字节码层面复用slot后,多个Lambda闭包意外共享了同一个存储位置。常见于循环中创建Lambda:
立即学习“Java免费学习笔记(深入)”;
for (int i = 0; i < 3; i++) {
list.add(() -> System.out.println(i));
}
结果全打印3——因为所有Lambda都读取了i所在的那个slot,而循环结束时slot里存的是最终值。
- 本质不是“变量被修改”,而是
i在整个循环中始终占用同一个slot,没有为每次迭代分配新位置 - 解决方式是手动制造作用域隔离:
final int j = i;或用IntStream.range(0,3).forEach(i -> ...) - Java 8+对循环变量做了一定优化,但仅限于
for-each和stream,传统for仍复用slot
调试时怎么看真实slot使用情况?
靠javap -v看Code段里的aload_0、iload_1这类指令,数字就是slot索引;再对照LocalVariableTable确认哪个变量名落在哪个区间。
-
iload_n(n=0~3)是快捷指令,超过就用iload+ 显式slot数 - 注意
astore/astore_0等指令写入slot,aload读取,方向反了会直接VerifyError - 如果方法里用了大量临时变量又很快丢弃,
slot会被反复覆盖,栈帧大小未必随变量声明数线性增长
slot复用行为跨版本一致,尤其涉及内联、逃逸分析或GraalVM等非HotSpot实现时,同一段Java代码生成的slot布局可能不同。别把slot编号当契约用。








