核心机制:JVM通过执行引擎逐条解释字节码指令,依托栈帧(Stack Frame)管理方法执行上下文。
1. 类加载与初始化
- 类加载器:将字节码文件加载至JVM,常量池内容(如字面量、符号引用)复制到运行时常量池(Runtime Constant Pool)。
- 大字面量处理:若常量值 >
short(如int/long),直接存入运行时常量池(无需额外处理)。
- 大字面量处理:若常量值 >
- 方法区(Method Area):存储编译后的字节码指令(方法代码)、类元数据、静态变量。
2. 栈帧(Stack Frame)结构
每个方法调用分配一个栈帧,包含:
- 局部变量表(Local Variable Table):
-
存储方法参数、局部变量(按类型分配空间):
类型 大小 占用槽位 byte/boolean1字节 1 char/short2字节 1 int/float4字节 1 long/double8字节 2(占两个槽位,非"切分")
-
- 操作数栈(Operand Stack):
- 临时存储操作数,用于运算(如
iadd、iload)。 - 关键规则:
- 变量从局部变量表加载到操作数栈(如
iload_0)。 - 运算结果从操作数栈弹出存回局部变量表(如
istore_0)。 long/double:操作时占用栈帧两个槽位(如lload读取long到两个槽位)。
- 变量从局部变量表加载到操作数栈(如
- 临时存储操作数,用于运算(如
3. 指令执行流程
- 指令加载:执行引擎从方法区读取字节码指令。
- 变量操作:
-
加载:
iload等指令从局部变量表→操作数栈。 -
运算:操作数栈中执行指令(如
iadd),结果暂存栈顶。 -
存储:
istore等指令将结果从栈顶弹出→局部变量表。示例:
int a = 1 + 2;iconst_1→ 操作数栈;iconst_2→ 操作数栈;iadd→ 栈顶得3;istore_0→ 存入局部变量表。
-
- 特殊指令:
getstatic:- 从运行时常量池查静态字段符号引用 → 堆中定位静态对象 → 引用压入操作数栈。
- 方法调用(如
invokevirtual):- 从常量池解析方法符号引用 → 分配新栈帧(含新局部变量表、操作数栈)→ 参数压栈。
4. 关键细节与优化
a++处理:-
JVM 用
iinc指令直接修改局部变量表(不经过操作数栈),避免额外栈操作。例:
a++;→iinc a 1(直接修改局部变量a)。
-
- 栈帧生命周期:
- 方法调用 → 创建栈帧;方法结束 → 弹出栈帧(释放资源)。
- 错误修正:
- ❌ 速写“变量分配4字节,不够补0” → ✅ 按类型分配(如
long8字节)。 - ❌ “
long切分成两端” → ✅ 占两个栈帧槽位(每个4字节,连续存储)。
- ❌ 速写“变量分配4字节,不够补0” → ✅ 按类型分配(如
总结:方法执行 = 加载指令 → 局部变量表 ↔ 操作数栈 → 运算 → 存储。栈帧是执行单元,a++ 为JVM优化指令(直接操作局部变量表),避免栈操作开销。