Để minh họa cách JVM thực thi phương thức ở cấp độ bytecode, ta xét một lớp Java đơn giản sau:
public class StackExecutionDemo {
private static Integer baseValue = 42;
public int computeResult(int input) {
int multiplier = 7;
int intermediate = input * multiplier;
multiplier += 3;
intermediate = baseValue + multiplier;
return intermediate;
}
public static void main(String[] args) {
System.out.println(new StackExecutionDemo().computeResult(5));
}
}
Sau khi biên dịch, phần bytecode tương ứng với phương thức computeResult như sau (đã rút gọn và làm rõ ngữ nghĩa):
public int computeResult(int);
descriptor: (I)I
flags: ACC_PUBLIC
Code:
stack=2, locals=4, args_size=2
0: bipush 7 // Đẩy hằng số 7 vào ngăn xếp toán tử
2: istore_2 // Lưu giá trị đỉnh ngăn xếp vào biến địa phương index 2 (multiplier)
3: iload_1 // Đẩy tham số 'input' (index 1) vào ngăn xếp
4: iload_2 // Đẩy 'multiplier' (index 2) vào ngăn xếp
5: imul // Nhân hai giá trị trên đỉnh ngăn xếp → kết quả vào ngăn xếp
6: istore_3 // Lưu kết quả vào biến địa phương index 3 (intermediate)
7: iinc 2, 3 // Tăng 'multiplier' (index 2) lên 3 đơn vị
10: getstatic #2 // Lấy tham chiếu tĩnh 'baseValue'
13: invokevirtual #3 // Gọi intValue() để chuyển Integer → int
16: iload_2 // Đẩy 'multiplier' đã cập nhật vào ngăn xếp
17: iadd // Cộng hai giá trị: baseValue + multiplier
18: istore_3 // Ghi đè giá trị vào 'intermediate'
19: iload_3 // Đẩy 'intermediate' ra ngăn xếp để trả về
20: ireturn
Quá trình thực thi được mô tả theo từng bước dựa trên trạng thái của ngăn xếp toán tử (operand stack) và bảng biến địa phương (local variable table), với kích thước lần lượt là 2 và 4 ô:
- Bước 0–2: Lệnh
bipush 7đẩy giá trị7vào ngăn xếp → ngăn xếp:[7]. Sau đóistore_2lấy giá trị này lưu vào ô thứ 2 của bảng biến địa phương →locals[2] = 7. - Bước 3–4:
iload_1vàiload_2lần lượt đẩyinput(giá trị truyền vào là5) vàmultiplier(7) vào ngăn xếp → ngăn xếp:[5, 7]. - Bước 5:
imulnhân hai giá trị, kết quả35thay thế hai toán hạng → ngăn xếp:[35].istore_3lưu giá trị này vàolocals[3]. - Bước 7:
iinc 2, 3tănglocals[2]từ7lên10— không ảnh hưởng đến ngăn xếp. - Bước 10–13: Truy xuất trường tĩnh
baseValue, gọiintValue()để ép kiểu → đưa giá trị nguyên42vào ngăn xếp. - Bước 16–17: Đẩy
locals[2](10) vào ngăn xếp, rồi dùngiaddcộng với42→ kết quả52. - Bước 18–20: Lưu
52vàolocals[3], tải lại và trả về bằngireturn.
Trong suốt quá trình, bảng biến địa phương luôn giữ vai trò lưu trữ các biến cục bộ và tham số, trong khi ngăn xếp toán tử phục vụ tính toán tạm thời — tất cả đều tuân thủ nghiêm ngặt quy tắc LIFO và ràng buộc kích thước đã khai báo trong phần Code của mỗi phương thức.