《深入Java虚拟机学习笔记》- 第10章 栈和局部变量操作
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
}
2通用栈操作
| 操作码 | 操作数 | 说明 | 
|---|---|---|
| nop | (无) | 不做任何操作 | 
| pop | (无) | 从操作数栈弹出栈顶部的一个字 | 
| pop2 | (无) | 从操作数栈弹出最顶端的两个字 | 
| swap | (无) | 交换栈顶部的两个字 | 
| dup | (无) | 复制栈顶部的一个字 | 
| dup2 | (无) | 复制栈顶部的两个字 | 
| dup_x1 | (无) | 复制栈顶部的一个字,并将复制内容及原来弹出的两个字长的内容压入栈 | 
| dup_x2 | (无) | 复制栈顶部的一个字,并将复制内容及原来弹出的三个字长的内容压入栈 | 
| dup2_x1 | (无) | 复制栈顶部的两个字,并将复制内容及原来弹出的三个字长的内容压入栈 | 
| dup2_x2 | (无) | 复制栈顶部的两个字,并将复制内容及原来弹出的四个字长的内容压入栈 | 
1,dup:复制栈顶部的一个字长的内容。
栈:
前:......,word
后:......,word,word
2,dup_x1:复制栈顶部一个字长的内容,然后将复制内容及原来弹出的两个字长的内容压入栈
栈:
前:......,word2,word1
后:......,word1,word2,word1
3,dup_x2:复制栈顶部一个字长的内容,然后将复制内容及原来弹出的三个字长的内容压入栈
栈:
前:.......,word3,word2,word1
后:.......,word1,word3,word2,word1
4,dup2:复制栈顶部长度为两个字长的内容
栈:
前:......,word2,word1
后:......,word2,word1,word2,word1
5,dup2_x1:复制栈顶部两个字长的内容,然后将复制内容及原来弹出的三个字长的内容压入栈
栈:
前:......,word3,word2,word1
后:.......,word2,word1,word3,word2,word1
6,dup2_x2:复制栈顶部两个字长的内容,然后将复制内容及原来弹出的四个字长的内容压入栈
栈:
前:......,word4,word3,word2,word1
后:.......,word2,word1,word4,word3,word2,word1
7,pop:弹出栈顶端一个字长的内容
栈:
前:......,word
后:.......
8,pop2:弹出栈顶端两个字长的内容
栈:
前:......,word2,word1
后:.......
9,swap:交换栈顶端两个字的内容
栈:
前:......,word2,word1
后:.......,word1,word2
例如如下代码:
public class StackTest {
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
String a;
String b;
a = new String("aaa");
b = new String("aaa");
}
}
用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: new #16; //class java/lang/String
3: dup
4: ldc #18; //String aaa
6: invokespecial #20; //Method java/lang/String."<init>":(Ljava/lang/String;)V
9: astore_1
10: new #16; //class java/lang/String
13: dup
14: ldc #18; //String aaa
16: invokespecial #20; //Method java/lang/String."<init>":(Ljava/lang/String;)V
19: astore_2
20: return
}
3,把局部变量压入栈
将一个字长的局部变量压入栈
| 操作码 | 操作数 | 说明 | 
|---|---|---|
| iload | vindex | 将位置为vindex的int类型的局部变量压入栈 | 
| iload_0 | (无) | 将位置为0的int类型的局部变量压入栈 | 
| iload_1 | (无) | 将位置为1的int类型的局部变量压入栈 | 
| iload_2 | (无) | 将位置为2的int类型的局部变量压入栈 | 
| iload_3 | (无) | 将位置为3的int类型的局部变量压入栈 | 
| fload | vindex | 将位置为vindex的float类型的局部变量压入栈 | 
| fload_0 | (无) | 将位置为0的float类型的局部变量压入栈 | 
| fload_1 | (无) | 将位置为1的float类型的局部变量压入栈 | 
| fload_2 | (无) | 将位置为2的float类型的局部变量压入栈 | 
| fload_3 | (无) | 将位置为3的float类型的局部变量压入栈 | 
将两个字长的局部变量压入栈
| 操作码 | 操作数 | 说明 | 
|---|---|---|
| lload | vindex | 将位置为vindex和(vindex+1)的long类型的局部变量压入栈 | 
| lload_0 | (无) | 将位置为0和1的long类型的局部变量压入栈 | 
| lload_1 | (无) | 将位置为1和2的long类型的局部变量压入栈 | 
| lload_2 | (无) | 将位置为2和3的long类型的局部变量压入栈 | 
| lload_3 | (无) | 将位置为3和4的long类型的局部变量压入栈 | 
| dload | vindex | 将位置为vindex和(vindex+1)的double类型的局部变量压入栈 | 
| dload_0 | (无) | 将位置为0和1的double类型的局部变量压入栈 | 
| dload_1 | (无) | 将位置为1和2的double类型的局部变量压入栈 | 
| dload_2 | (无) | 将位置为2和3double类型的局部变量压入栈 | 
| dload_3 | (无) | 将位置为3和4double类型的局部变量压入栈 | 
将对象引用局部变量压入栈
| 操作码 | 操作数 | 说明 | 
|---|---|---|
| aload | vindex | 将位置为vindex的对象引用局部变量压入栈 | 
| aload_0 | (无) | 将位置为0的对象引用局部变量压入栈 | 
| aload_1 | (无) | 将位置为1的对象引用局部变量压入栈 | 
| aload_2 | (无) | 将位置为2的对象引用局部变量压入栈 | 
| aload_3 | (无) | 将位置为3的对象引用局部变量压入栈 | 
4,弹出栈顶元素,将其赋给局部变量
弹出一个字长的值,将其赋给局部变量
| 操作码 | 操作数 | 说明 | 
|---|---|---|
| istore | vindex | 从栈中弹出int类型值,然后将其存到位置为vindex的局部变量中 | 
| istore_0 | (无) | 从栈中弹出int类型值,然后将其存到位置为0的局部变量中 | 
| istore_1 | (无) | 从栈中弹出int类型值,然后将其存到位置为1的局部变量中 | 
| istore_2 | (无) | 从栈中弹出int类型值,然后将其存到位置为2的局部变量中 | 
| istore_3 | (无) | 从栈中弹出int类型值,然后将其存到位置为3的局部变量中 | 
| fstore | vindex | 从栈中弹出float类型值,然后将其存到位置为vindex的局部变量中 | 
| fstore_0 | (无) | 从栈中弹出float类型值,然后将其存到位置为0的局部变量中 | 
| fstore_1 | (无) | 从栈中弹出float类型值,然后将其存到位置为1的局部变量中 | 
| fstore_2 | (无) | 从栈中弹出float类型值,然后将其存到位置为2的局部变量中 | 
| fstore_3 | (无) | 从栈中弹出float类型值,然后将其存到位置为3的局部变量中 | 
弹出对象引用,并将其赋值给局部变量
| 操作码 | 操作数 | 说明 | 
|---|---|---|
| lstore | vindex | 从栈中弹出long类型值,然后将其存到位置为vindex和(vindex+1)的局部变量中 | 
| lstore_0 | (无) | 从栈中弹出long类型值,然后将其存到位置为0和1的局部变量中 | 
| lstore_1 | (无) | 从栈中弹出long类型值,然后将其存到位置为1和2的局部变量中 | 
| lstore_2 | (无) | 从栈中弹出long类型值,然后将其存到位置为2和3的局部变量中 | 
| lstore_3 | (无) | 从栈中弹出long类型值,然后将其存到位置为3和4的局部变量中 | 
| dstore | vindex | 从栈中弹出double类型值,然后将其存到位置为vindex和(vindex+1)的局部变量中 | 
| dstore_0 | (无) | 从栈中弹出double类型值,然后将其存到位置为0和1的局部变量中 | 
| dstore_1 | (无) | 从栈中弹出double类型值,然后将其存到位置为1和2的局部变量中 | 
| dstore_2 | (无) | 从栈中弹出double类型值,然后将其存到位置为2和3的局部变量中 | 
| dstore_3 | (无) | 从栈中弹出double类型值,然后将其存到位置为3和4的局部变量中 | 
| 操作码 | 操作数 | 说明 | 
|---|---|---|
| astore | vindex | 从栈中弹出对象引用,然后将其存到位置为vindex的局部变量中 | 
| astore_0 | (无) | 从栈中弹出对象引用,然后将其存到位置为0的局部变量中 | 
| astore_1 | (无) | 从栈中弹出对象引用,然后将其存到位置为1的局部变量中 | 
| astore_2 | (无) | 从栈中弹出对象引用,然后将其存到位置为2的局部变量中 | 
| astore_3 | (无) | 从栈中弹出对象引用,然后将其存到位置为3的局部变量中 | 
5,wide指令
无符号8位局部变量索引,把方法中局部变量数的限制在256以下。一条单独的wide指令可以将8位的索引再扩展8位,就可以把局部变量数的限制扩展到65536.
| 操作码 | 操作数 | 说明 | 
|---|---|---|
| wide | iload,index | 从局部变量位置为index的地方取出int类型值,并将其压入栈 | 
| wide | lload ,index | 从局部变量位置为index的地方取出long类型值,并将其压入栈 | 
| wide | fload,index | 从局部变量位置为index的地方取出float类型值,并将其压入栈 | 
| wide | dload,index | 从局部变量位置为index的地方取出double类型值,并将其压入栈 | 
| wide | aload,index | 从局部变量位置为index的地方取出对象引用,并将其压入栈 | 
| wide | istore,index | 从栈中弹出int类型值,将其存入位置为index的局部变量中 | 
| wide | lstore,index | 从栈中弹出long类型值,将其存入位置为index的局部变量中 | 
| wide | fstore,index | 从栈中弹出float类型值,将其存入位置为index的局部变量中 | 
| wide | dstore,index | 从栈中弹出double类型值,将其存入位置为index的局部变量中 | 
| wide | astore,index | 从栈中弹出对象引用,将其存入位置为index的局部变量中 | 
跳转指令并不允许直接跳转到被wide指令修改过的操作码。
《深入Java虚拟机学习笔记》- 第10章 栈和局部变量操作的更多相关文章
- 《深入Java虚拟机学习笔记》- 第19章 方法的调用与返回
		<深入Java虚拟机学习笔记>- 第19章 方法的调用与返回 
- 《深入Java虚拟机学习笔记》- 第16章 控制流
		<深入Java虚拟机学习笔记>- 第16章 控制流 
- 《深入Java虚拟机学习笔记》- 第17章 异常
		<深入Java虚拟机学习笔记>- 第17章 异常 
- 《深入Java虚拟机学习笔记》- 第13章 逻辑运算
		<深入Java虚拟机学习笔记>- 第13章 浮点运算 
- 《深入Java虚拟机学习笔记》- 第14章 浮点运算
		<深入Java虚拟机学习笔记>- 第13章 浮点运算 
- 《深入Java虚拟机学习笔记》- 第8章   连接模型
		Java虚拟机学习笔记(八)连接模型 
- 《深入Java虚拟机学习笔记》- 第4章  网络移动性
		Java虚拟机学习笔记(四)网络移动性 
- 《深入Java虚拟机学习笔记》- 第2章 平台无关
		Java虚拟机学习笔记(二)平台无关 
- java JDK8 学习笔记——第16章 整合数据库
		第十六章 整合数据库 16.1 JDBC入门 16.1.1 JDBC简介 1.JDBC是java联机数据库的标准规范.它定义了一组标准类与接口,标准API中的接口会有数据库厂商操作,称为JDBC驱动程 ... 
随机推荐
- ios 下引用第三方类库报错
			在最近刚接触的ios的一个项目中用到了腾讯的登录sdk,从git下下来之后编译报错,找不到文件,文件明明已经加入到项目中,为何找不到,由于刚接触ios开发,所有不知道什么原因,经过Google才知道 ... 
- linux mint 五笔安装方法
			终于可以使用五笔了,方法就是安装好ibus后要设置首选项,这样在首选项里设置就可以了. 
- C#中字符串驻留技术
			转自:http://www.cnblogs.com/Charles2008/archive/2009/04/12/1434115.html MSDN概念:公共语言运行库通过维护一个表来存放字符串,该表 ... 
- OpenSessionInViewFilter 的配置及替代方案(转)
			鸣谢:http://justsee.iteye.com/blog/1174999,http://blog.csdn.net/sunsea08/article/details/4545186 Sprin ... 
- Hibernate注解方法使用总结
			1.类级别注解 @Entity 映射实体类 @Table 映射数句库表 @Entity(name="tableName") - 必须,注解将一个类声明为一个实体bea ... 
- leetcode5	Implement strstr() 实现strstr函数功能
			Implement strstr() 实现strstr函数功能 whowhoha@outlook.com Question: Implement strstr(). Returns the index ... 
- html代码究竟什么用途
			1.html代码,只能浏览器识别并读出.渲染出网页图形 2.html代码可以本地写,用浏览器渲染出.也可以服务器端通过http协议传送过来,在网页显示. 咱们上网看的网页都是服务器端通过http协议传 ... 
- WPF之Behavior
			本文主要是以实现拖动元素作为例子. 创建Behavior: 通常这个类会继承自Behavior<T>,其中T就是此Behavior服务的对象,在此处使用的是UIElement,也就是虽有的 ... 
- Python高级特性
			比如构造一个1, 3, 5, 7, ..., 99的列表,可以通过循环实现: 这是正确的姿势 
- 函数重载二义性:error C2668: 'pow' : ambiguous call to overloaded function
			2013-07-08 14:42:45 当使用的函数时重载函数时,若编译器不能判断出是哪个函数,就会出现二义性,并给出报错信息. 问题描述: 在.cpp代码中用到pow函数,如下: long int ... 
