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)对象引用压入栈

例如下面代码:

publicclass
StackTest {
 
	/**
	
* @param
args
	
*/
	publicstaticvoid
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范围的位置。

例如下面代码:

publicclass
StackTest {
 
	/**
	
* @param
args
	
*/
	publicstaticvoid
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


}