文章目录
  1. JVM学习05-字节码执行过程和JVM指令集
  2. 1. 字节码的Code部分
  3. 3. JVM指令集
    1. 3.1 常量入栈
    2. 3.2 局部变量操作
    3. 3.3 通用栈操作
    4. 3.4 类型转化
    5. 3.5 整数运算和浮点运算
    6. 3.6 对象操作指令和方法调用指令
    7. 3.7 条件控制

[TOC]

JVM学习05-字节码执行过程和JVM指令集

1. 字节码的Code部分

在执行字节码的时候,无非也就是对调用类中的函数。那么下面将介绍下字节码函数的Code部分,Code部分的代码一个可以用java自带的命令javap命令进行查看。还可以在eclipse中安装ByteCode visualizer插件查看,具体使用自行研究。

在我介绍java内存模型的时候,函数的执行过程是分配在栈内存中的,所以在执行Code部分的时候肯定会涉及到局部变量表和操作数栈,同时还会涉及到程序计数器。之前也介绍了一个小例子,下面继续先以一个小例子讲述。

#2. 例子

源码:

1
2
3
4
5
6
7
8
9
10
package com.minosa.test;
public class HelloClass {

public int foo(){
int a = 1;
int b = 2;
int c = a + b;
return c;
}
}

ByteCode visualizer查看

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
public int foo() {
/* L6 */
0 iconst_1;
1 istore_1; /* a */
/* L7 */
2 iconst_2;
3 istore_2; /* b */
/* L8 */
4 iload_1; /* a */
5 iload_2; /* b */
6 iadd;
7 istore_3; /* c */
/* L9 */
8 iload_3; /* c */
9 ireturn;
/* LineNumberTable */
/* ----------+------------- */
/* start_pc | line_number */
/* ----------+------------- */
/* 0 | 6 */
/* 2 | 7 */
/* 4 | 8 */
/* 8 | 9 */
/* ----------+------------- */
/* LocalVariableTable */
/* -------+----------+--------+------------+------------------ */
/* index | start_pc | length | name_index | descriptor_index */
/* -------+----------+--------+------------+------------------ */
/* 0 | 0 | 10 | 12 | 13 */
/* 1 | 2 | 8 | 16 | 17 */
/* 2 | 4 | 6 | 18 | 17 */
/* 3 | 8 | 2 | 19 | 17 */
/* -------+----------+--------+------------+------------------ */
/* ExceptionTable (empty) */
/* max_stack: 2 max_locals: 4 */
}

上面在字节码的文件结构中讲过,每个方法中存在很多属性,例如LineNumberTable,LocalVariableTable 等等。里面的一些”_index”就是指向常量池中的索引。对于成员方法来说(非类方法),局部变量表中的第一个是this(之前有讲过)。
这里抽出最关键的Code进行讲述:
由上面的LocalVariableTable可以知道,a变量的index是1,b是2,c是3。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public int foo() {
/* L6 */
0 iconst_1;
1 istore_1; /* a */
/* L7 */
2 iconst_2;
3 istore_2; /* b */
/* L8 */
4 iload_1; /* a */
5 iload_2; /* b */
6 iadd;
7 istore_3; /* c */
/* L9 */
8 iload_3; /* c */
9 ireturn;
}

1. int a = 1;
首先程序计数器值为0,iconst_1指令将整数1压入操作数栈中;执行istore_1,程序计数器为1,将操作数栈中的数弹出,然后赋值给索引为1的变量(即a)。

2. int b = 2;
跟 int a = 1; 基本一致。

3. int c = a + b;
这里iload指令(i表示int型,后面会列出常用的指令表)将下标为1和2,即a和b压入栈中。然后执行 idd 指令,弹出两个操作数进行相加,并将结果值压入操作数栈中。最后istore_3指令将栈中的操作数弹出并赋值给变量c。

4. return c;
这里先将c的值载入压入到栈中,然后执行ireturn指令。

这里有一个很经典的面试题:就是try中的return和finally中的return,两个return的执行流程。其实看一下反汇编的指令就一目了然了。

3. JVM指令集

具体的指令集可以参照博文:
http://blog.csdn.net/lm2302293/article/details/6713147

里面描述了指令在字节码中对应的Byte,助记符以及功能描述,下面将对常用进行描述下。

3.1 常量入栈

1. aconst_null
null对象入栈,前面的a表示对象ref。

2. iconst_m1
将 -1 压入栈中。其他的byte型和short型参照 bipush 和 sipush 指令

3. iconst_0 ~ iconst_5
将整数 0 ~ 5 压入栈中。其他的byte型和short型参照 bipush 和 sipush 指令

4. lconst_1 ~ lconst_2
将long类型常量 1或2 压入栈中,其他参照 ldc2_w 指令;

5. fconst_1 ~ fconst_2
将float类型常量 1或2 压入栈中,其他参照 ldc 指令。

6. dconst_1 ~ dconst_1
将double类型常量 1或2 压入栈中,其他参照 ldc2_w 指令。

7. bipush 和 sipush
bipush将一个byte的带符号常量压入栈中,sipush将一个short型带符号常量压入栈中

8. ldc,ldc_w和ldc2_w
ldc 将int、float或String型常量值从常量池中压入栈中。
ldc_w将int、float或String型常量值从常量池中压入栈中(宽索引)。
ldc2_w将long或double型常量值从常量池中压入到栈中(宽索引)。

3.2 局部变量操作

1. load(为i,l,f,d和a中一个)
分别将int型,long型,float型,double型以及Object ref型的局部变量压入栈中。指令后面继续跟着_0,_1,_2,_3表示将索引为0 ~ 3的局部变量压入到栈中,大于3的索引则去掉下划线”_”,如”iload_1”,”iload 4”。

2. aload(为c,b,s,i,l,f,d和a中一个)
将指定类型数组中的值压入到栈中。取值的时候先将数组的ref压入栈,然后是需要获取数据的index,然后执行*aload指令,并将获取到的值压入到栈中。这里可能会抛出数组下标越界的异常。

3. store 和 astore 指令
这里基本和 load 和 aload指令类似。

3.3 通用栈操作

1. nop
什么都不做。

2. pop
从栈顶弹出一个字长。

3. dup
赋值栈顶一个字长,复制内容压入栈中。

3.4 类型转化

i2c、i2b、i2s、i2l,i2f,l2i,l2f,l2d,f2i,f2d,d2i,d2l,d2f。

3.5 整数运算和浮点运算

1
2
3
4
5
6
7
8
9
10
11
12
iadd,ladd,fadd,dadd  加 +
isub,lsub,fsub,dsub 减 -
imul,lmul,fmul,dmul  乘 *
idiv, ldiv,fdiv,ddiv  除 /
irem,lrem,frem,drem  取模 %
ineg,lneg,fneg,dneg 取负 -
ishl,lshl         左移 <<
ishr,lshr,iushr,lushr 有符号和无符号右移 >>和>>>
iand,land 按位与 &
ior,lor 按位或 |
ixor,lxor 按位异或 ^
iinc 指定int型变量增加指定的值

 

3.6 对象操作指令和方法调用指令

1. new
创建一个对象,并将引用压入栈中。

2. invokespecial 和 invokevirtual
调用构造方法,私有方法

3. invokestatic 和 invokeinterface
调用静态方法和接口方法

4. getstatic 和 putstatic
getstatic 获取指定类的静态域并将值压入栈中;putstatic为指定静态域赋值。

5. getfield 和 putfield
同上,只是针对实例域

6. *return
(return前面的*可表示为 i l f d a 或(为空return即void))

3.7 条件控制

ifeq,ifne,if_icmpeq 等等一些指令。详细见指令表。

文章目录
  1. JVM学习05-字节码执行过程和JVM指令集
  2. 1. 字节码的Code部分
  3. 3. JVM指令集
    1. 3.1 常量入栈
    2. 3.2 局部变量操作
    3. 3.3 通用栈操作
    4. 3.4 类型转化
    5. 3.5 整数运算和浮点运算
    6. 3.6 对象操作指令和方法调用指令
    7. 3.7 条件控制