转:http://www.cnblogs.com/chenqiangjsj/archive/2011/04/02/2003892.html
Java栈和局部变量操作
Java虚拟机是基于栈的机器,几乎所有Java虚拟机的指令都与操作数栈相关。栈操作包括把常量压入操作数栈、执行通用的栈操作、在操作数栈和局部变量之间往返传输值。
1常量入栈操作:
操作码在执行常量入栈操作之前,使用三种方式指明常量的值:常量值隐含包含在操作码内部、常量值在字节码中如同操作数一样跟随在操作码之后,或者从常量池中取出常量。
1.1常量值隐含包含在操作码内部:
将一个字长的常量压入栈
操作码 | 操作数 | 说明 |
---|---|---|
iconst_m1 | (无) | 将int类型值-1压入栈 |
iconst_0 | (无) | 将int类型值0压入栈 |
iconst_1 | (无) | 将int类型值1压入栈 |
iconst_2 | (无) | 将int类型值2压入栈 |
iconst_3 | (无) | 将int类型值3压入栈 |
iconst_4 | (无) | 将int类型值4压入栈 |
iconst_5 | (无) | 将int类型值5压入栈 |
fconst_0 | (无) | 将float类型值0压入栈 |
fconst_1 | (无) | 将float类型值1压入栈 |
fconst_2 | (无) | 将float类型值2压入栈 |
将两个字长的常量压入栈
操作码 | 操作数 | 说明 |
---|---|---|
lconst_0 | (无) | 将long类型值0压入栈 |
lconst_1 | (无) | 将long类型值1压入栈 |
dconst_0 | (无) | 将double类型值0压入栈 |
dconst_1 | (无) | 将double类型值1压入栈 |
给一个对象引用赋空值时会用到aconst_null指令
将空(null)对象引用压入栈
操作码 | 操作数 | 说明 |
---|---|---|
aconst_null | (无) | 将空(null)对象引用压入栈 |
例如下面代码:
public class StackTest {
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
int i = 0;
int j = 4;
int k;
k = i + j;
float a = 0;
float b = 1;
float c = a + b;
long x = 0;
long y = 1;
long z = x + y;
String string = null;
}
}
用javap工具查看其字节码为:
Compiled from "StackTest.java"
public class StackTest extends java.lang.Object{
public StackTest();
Code:
0: aload_0
1: invokespecial #8; //Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]);
Code:
0: iconst_0 //常量int类型的0入栈
1: istore_1 //弹出栈顶元素0存入位置1的局部变量中
2: iconst_4 //常量int类型的4入栈
3: istore_2 //弹出栈顶元素4存入位置2的局部变量中
4: iload_1 //从位置为1的局部变量中取出元素int类型的0压入栈
5: iload_2 //从位置为2的局部变量中取出元素int类型的4压入栈
6: iadd //从栈顶弹出两个元素然后做加法,把结果压入栈
7: istore_3 //弹出栈顶元素4存入位置为3的局部变量中
8: fconst_0 //常量float类型的0入栈
9: fstore 4 //弹出栈顶元素0存入位置为4的局部变量中
11: fconst_1 //常量float类型的1入栈
12: fstore 5 //弹出栈顶元素1存入位置为5的局部变量中
14: fload 4 //从位置为4的局部变量中取出元素float类型的0压入栈
16: fload 5 //从位置为5的局部变量中取出元素float类型的1压入栈
18: fadd //从栈顶弹出两个元素然后做加法,把结果压入栈
19: fstore 6 //弹出栈顶元素1存入位置为3的局部变量中
21: lconst_0 //常量long类型的0入栈
22: lstore 7 // 弹出栈顶元素0存入位置为7和8的局部变量中
24: lconst_1 //常量long类型的1入栈
25: lstore 9 // 弹出栈顶元素0存入位置为9和10的局部变量中
27: lload 7 //从位置为7和8的局部变量中取出元素long类型的0压入栈
29: lload 9 //从位置为9和10的局部变量中取出元素long类型的1压入栈
31: ladd //从栈顶弹出两个元素然后做加法,把结果压入栈
32: lstore 11 //弹出栈顶元素1存入位置为11和12的局部变量中
34: aconst_null //将null对象引用压入栈
35: astore 13 //弹出栈顶元素null存入位置为13的局部变量中
37: return
}
1.2常量值在字节码中跟随在操作码之后:
将byte和short类型常量压入栈
操作码 | 操作数 | 说明 |
---|---|---|
bipush | 一个byte类型的数 | 将byte类型的数转换为int类型的数,然后压入栈 |
sipush | 一个short类型的数 | 将short类型的数转换为int类型的数,然后压入栈 |
1.3从常量池中取出常量
操作码 | 操作数 | 说明 |
---|---|---|
ldc | 无符号8位数indexbyte | 从由indexbyte指向的常量池入口中取出一个字长的值,然后将其压入栈 |
ldc_w | 无符号16位数indexshort | 从由indexshort指向的常量池入口中取出一个字长的值,然后将其压入栈 |
ldc2_w | 无符号16位数indexshort | 从由indexshort指向的常量池入口中取出两个字长的值,然后将其压入栈 |
这三个操作码是从常量池中取出常量,然后将其压入栈,这些操作码的操作码表示常量池索引,Java虚拟机通过给定的索引查找相应的常量池入口,决定这些常量的类型和值,并把它们压入栈。
常量池索引是一个无符号值,ldc和ldc_w是把一个字长的项压入栈,区别在于:ldc的索引只有一个8位,只能指向常量池中1~255范围的位置。ldc_w的索引有16位,可以指向1~65535范围的位置。
例如下面代码:
public class StackTest {
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
byte i = 125;
byte j = -128;
int k = i + j;
short a = 32767;
short b = - 32768;
int c = a + b;
int x = 2147483647;
int y = -2147483648;
int z = x + y;
long I = 2147483648L;
long J = -2147483649L;
long K = I + J;
}
}
用javap工具查看其字节码为:
Compiled from "StackTest.java"
public class StackTest extends java.lang.Object{
public StackTest();
Code:
0: aload_0
1: invokespecial #8; //Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]);
Code:
0: bipush 125 //将byte类型的255转换成int类型压入栈
2: istore_1 //弹出栈顶元素255存入位置为1的局部变量中
3: bipush -128 //将byte类型的-128转换成int类型压入栈
5: istore_2 //弹出栈顶元素-128存入位置为2的局部变量中
6: iload_1 //取出位置为1的局部变量中的数压入栈
7: iload_2 //取出位置为2的局部变量中的数压入栈
8: iadd //从栈顶弹出两个元素然后做加法,把结果压入栈
9: istore_3 //弹出栈顶元素存入位置为3的局部变量中
10: sipush 32767 //将short类型的32767转换成int类型压入栈
13: istore 4 //弹出栈顶元素32767存入位置为4的局部变量中
15: sipush -32768 /将short类型的-32768转换成int类型压入栈
18: istore 5 //弹出栈顶元素-32768存入位置为5的局部变量中
20: iload 4 //取出位置为4的局部变量中的数压入栈
22: iload 5 //取出位置为5的局部变量中的数压入栈
24: iadd //从栈顶弹出两个元素然后做加法,把结果压入栈
25: istore 6 /弹出栈顶元素存入位置为6的局部变量中
27: ldc #16; //int 2147483647 //从常量池索引16的位置取出2147483647压入栈
29: istore 7 //弹出栈顶元素2147483647存入位置为4的局部变量中
31: ldc #17; //int -2147483648 //从常量池索引17的位置取出-2147483648压入栈
33: istore 8 //弹出栈顶元素-2147483648存入位置为8的局部变量中
35: iload 7 //取出位置为7的局部变量中的数压入栈
37: iload 8 //取出位置为8的局部变量中的数压入栈
39: iadd //从栈顶弹出两个元素然后做加法,把结果压入栈
40: istore 9 //弹出栈顶元素存入位置为9的局部变量中
42: ldc2_w #18; //long 2147483648l //从常量池索引18的位置取出long类型的2147483648L压入栈
45: lstore 10 //弹出栈顶元素2147483648L存入位置为10和11的局部变量中
47: ldc2_w #20; //long -2147483649l //从常量池索引20的位置取出long类型的-2147483649L压入栈
50: lstore 12 //弹出栈顶元素-2147483649L存入位置为12和13的局部变量中
52: lload 10 //取出位置为10和11的局部变量中的数压入栈
54: lload 12 //取出位置为12和13的局部变量中的数压入栈
56: ladd //从栈顶弹出两个元素然后做加法,把结果压入栈
57: lstore 14 //弹出栈顶元素存入位置为14和15的局部变量中
59: return
}