java源码如下:
public static void main(String[] args) {
int a = 10;
int b = 1000;
System.out.println(add(a, b));
}
public static int add(int a, int b) {
int c = a + b;
return c;
}
解析字节码后:
public class Taaa {
public Taaa();
Code:
0: aload_0 //将局部变量表0号位置(this)入栈 实例方法局部变量表的第一个元素一定是对象的引用(this)
1: invokespecial #1 // Method java/lang/Object."<init>":()V 调用私有的实例化方法
4: return
public static void main(java.lang.String[]);
Code:
0: bipush 10 //将操作数10转为int类型入栈,就是源码里的变量a
2: istore_1 //将操作数栈 栈顶元素加载到局部变量表索引为1的这个位置
3: sipush 1000 //将操作数1000转int 入栈,源码里的b
6: istore_2 //同 istore_1 只是索引为2
7: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream; 将运行时常量池的#2元素入栈
10: iload_1 //读取局部变量表元素1到栈顶
11: iload_2 //读取局部变量表元素2到栈顶
12: invokestatic #3 // Method add:(II)I 调用静态方法add
15: invokevirtual #4 // Method java/io/PrintStream.println:(I)V 调用输出
18: return
public static int add(int, int);
Code:
0: iload_0 //在add这个方法里面,有一个新的局部变量和新的操作数栈,所以源码里的a和b分别作为局部变量表的0和1元素,这行是加载第0个元素到栈顶
1: iload_1 //这行是加载第1个元素到栈顶
2: iadd //将栈的最前面2个元素相加 将结果入栈(栈顶)
3: istore_2 //将栈顶元素存入局部变量表第2个位置
4: iload_2 //读取局部变量表第2个位置的元素加载到栈顶
5: ireturn //返回栈顶的int类型数据
}
这里有几个小细节,在源码同样都是int类型的 a 和b 在字节码分别用 bipush sipush载入。说明源码里的值被按照其取值范围窄化为小类型了。
局部变量表和操作数栈在每个方法被调用的时候创建,方法的参数顺序就是加入到局部变量表的顺序,比如add方法,a存在局部变量表的0位置,b存在1位置
每一行字节码开头的数值是本行相对于方法起始处的字节偏移量
静态方法局部变量表从0开始,方法的入参依次存入。
实例方法局部变量表从1开始,方法的入参从1开始存。0存了对象的引用
add方法偏移3、4那2行, 先将计算结果从栈顶存入局部变量表,又从局部变量表加载到栈顶,然后才返回栈顶。看上去是在重复计算,是因为源码没有直接 return a+b;
而这个字节码还没经过jvm的优化,只是完完全全的翻译源代码。所以写代码的时候尽量简单点。
网友评论