(十九)加载指令
一、概念
加载和存储指令用于将数据在栈帧中的局部变量表和操作数栈之间来回传输,这类指令包括如下内容。
- 将一个局部变量加载到操作栈:
iload、iload_<n>、lload、lload_<n>、fload、fload_<n>、dload、dload_<n>、aload、aload_<n>。
- 将一个数值从操作数栈存储到局部变量表:
istore、istore_<n>、lstore、lstore_<n>、fstore、fstore_<n>、dstore、dstore_<n>、astore、astore_<n>。
- 将一个常量加载到操作数栈:
bipush、sipush、ldc、ldc_w、ldc2_w、aconst_null、iconst_ml、iconst_<i>、lconst_<l>、fconst_<f>、dconst_<d>。
- 扩充局部变量表的访问索引的指令:
wide。
上面所列举的指令的指令助记符中,有一部分是以尖括号结尾的(例如iload_<n>),这些指令助记符实际上是代表了一组指令(例如iload_<n>,他代表了iload_0、iload_1、iload_2和iload_3这几条指令)。这几组指令都是某个带有一个操作数的通用指令(例如iload)的特殊形式,对于这若干组特殊指令来说,他们省略掉了显示的操作数,不需要进行取操作数的动作,实际上操作数就隐含在指令中。除了这点之外,他们的语义与原生的通用指令完全一致(例如iload_0的语义与操作数为0时的iload指令语义完全一致)。这种指令表示方法在本书以及《Java虚拟机规范》中都是通用的。
二、案例
- 源码如下:
public class Test { public int addNumber(int a,int b){ int result = a + b; String c = "admin"; return 2+2; } }
- javap查看如下:
C:\Users\Administrator\Desktop>javap -verbose Test.class Classfile /C:/Users/Administrator/Desktop/Test.class Last modified 2018-5-20; size 269 bytes MD5 checksum e26c0d715552b3c00c071a044e16d7a9 Compiled from "Test.java" public class Test minor version: 0 major version: 52 flags: ACC_PUBLIC, ACC_SUPER Constant pool: #1 = Methodref #4.#13 // java/lang/Object."<init>":()V #2 = String #14 // admin #3 = Class #15 // Test #4 = Class #16 // java/lang/Object #5 = Utf8 <init> #6 = Utf8 ()V #7 = Utf8 Code #8 = Utf8 LineNumberTable #9 = Utf8 addNumber #10 = Utf8 (II)I #11 = Utf8 SourceFile #12 = Utf8 Test.java #13 = NameAndType #5:#6 // "<init>":()V #14 = Utf8 admin #15 = Utf8 Test #16 = Utf8 java/lang/Object { public Test(); descriptor: ()V flags: ACC_PUBLIC Code: stack=1, locals=1, args_size=1 0: aload_0 1: invokespecial #1 // Method java/lang/Object."<init>":()V 4: return LineNumberTable: line 2: 0 public int addNumber(int, int); descriptor: (II)I flags: ACC_PUBLIC Code: stack=2, locals=5, args_size=3 0: iload_1 1: iload_2 2: iadd 3: istore_3 4: ldc #2 // String admin 6: astore 4 8: iconst_4 9: ireturn LineNumberTable: line 6: 0 line 8: 4 line 10: 8 } SourceFile: "Test.java"
- 如上代码金色标注,args_size=3,但是查看源码参数数量只有2个(int a, int b) ,其实会自动加一个this引用
-
0: iload_1 表示将第一个局部变量(a)压入操作数栈中.
-
1: iload_2表示将第二个局部变量(b)压入操作数栈中.
-
2: iadd 执行add操作
-
3:istore_3 将操作数栈的值弹出,即(a+b)的值并赋值给第3个局部变量result
-
4: ldc 将常量(“admin”)压入到操作数栈中。
-
6: astore 将操作数栈的值弹出,即(“admin”)赋值给c
-
8: iconst_4 将常量(4,编译器优化,识别2和2都是常量后,会先进行加法操作)压入操作数栈中
-
9: ireturn
Tip: 当int取值-1~5采用iconst指令,取值-128~127采用bipush指令,取值-32768~32767采用sipush指令,取值-2147483648~2147483647采用 ldc 指令。