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)对象引用压入栈
|
例如下面代码:
publicclassStackTest
{
/**
*
@paramargs
*/
publicstaticvoidmain(String[]
args) {
//TODOAuto-generated method
stub
inti = 0;
intj = 4;
intk;
k= i + j;
floata = 0;
floatb = 1;
floatc
=a + b;
longx = 0;
longy = 1;
longz
=x + y;
Stringstring=
null;
}
}
用javap工具查看其字节码为:
Compiledfrom "StackTest.java"
publicclass StackTest extends java.lang.Object{
publicStackTest();
Code:
0: aload_0
1: invokespecial #8;//Method java/lang/Object."<init>":()V
4: return
publicstatic 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范围的位置。
例如下面代码:
publicclassStackTest
{
/**
*
@paramargs
*/
publicstaticvoidmain(String[]
args) {
//TODOAuto-generated method
stub
bytei = 125;
bytej = -128;
int
k =i + j;
shorta = 32767;
shortb = - 32768;
intc
=a + b;
int x = 2147483647;
inty = -2147483648;
intz
=x + y;
longI = 2147483648L;
longJ = -2147483649L;
longK
=I + J;
}
}
用javap工具查看其字节码为:
Compiledfrom "StackTest.java"
publicclass StackTest extends java.lang.Object{
publicStackTest();
Code:
0: aload_0
1: invokespecial #8;//Method java/lang/Object."<init>":()V
4: return
publicstatic 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
}
分享到:
相关推荐
浅谈java中的局部变量和全局变量 Java 中的变量可以分为两大类:局部变量和全局变量。理解这两种变量的概念、生存时间和创建位置是java开发者必须具备的基本知识。下面我们将对java中的局部变量和全局变量进行详细...
Java栈是一种基于后进先出(LIFO)原则的数据结构,它在计算机科学和编程中具有广泛的应用。本文将深入探讨Java中栈的实现以及其在实际应用中的使用。 首先,我们来理解栈的基本概念。栈是一种特殊类型的线性数据...
Java栈是Java虚拟机(JVM)内存模型的重要组成部分,主要负责存储方法调用过程中的局部变量、操作数和方法返回信息。栈的特点是后进先出(LIFO),每个线程都有自己的独立Java栈,确保了线程安全的数据存储。 1. **...
由于每个线程都有自己的独立的调用栈,因此,从理论上来讲,局部变量在多线程环境中是线程安全的,因为每个线程都拥有自己独立的一份副本。 然而,实际的情况可能更为复杂。例如,如果局部变量引用了非线程安全的...
- **栈内存**:固定分配,大小在编译时确定,用于存储局部变量和方法调用信息。 ##### 2. 生命周期的比较 - **堆内存**:对象的生命周期不确定,由垃圾回收器决定何时回收。 - **栈内存**:变量的生命周期与方法的...
每个方法都对应一个栈桢,栈桢包含了方法的局部变量、操作数栈、方法返回地址等信息。在Java程序执行过程中,JVM会根据方法的调用关系创建和销毁栈桢。 二、栈桢的结构 栈桢的结构可以分为以下几个部分: * 局部...
栈是一种先进后出(LIFO,Last In First Out)的数据结构,主要用于存储方法调用时的信息,如局部变量、操作数栈、动态链接、方法出口等。Java中的栈具有以下特点: - **速度快**:栈的存取速度仅次于CPU寄存器,...
总的来说,Java函数调用涉及到内存的多个层次,包括堆上的对象分配、栈上的局部变量和操作数管理,以及帧数据区的组织。理解这些机制对于优化代码、排查问题以及提升程序性能至关重要。在实际开发中,熟练掌握这些...
每当程序执行到一个方法时,Java虚拟机会为这个方法分配一块新的栈帧,用于存储该方法内的所有局部变量。这些局部变量包括基本数据类型(如`int`、`double`等)以及对象的引用。 ##### 特点 - **作用域限定**:栈...
当在代码中声明一个局部变量时,Java会在栈中为其分配空间。一旦变量超出其作用域,Java会自动释放相应的内存空间,以便其他变量使用。栈内存的存取速度非常快,仅次于CPU的寄存器,因为它的操作是线性的,不需要像...
在Java卡虚拟机中,Java栈用于存储计算过程中的局部变量和操作数。每个方法调用都会在Java栈上创建一个栈帧,包含方法的局部变量、操作数、返回地址等信息。Java栈的管理是解释器的重要职责,包括栈帧的分配、数据的...
每当调用一个方法时,都会在栈上创建一个新的栈帧来保存该方法的信息,如局部变量、参数和返回地址等。 - **堆空间**:堆是所有线程共享的一块内存区域,用于存储对象的实例。JVM通过垃圾回收机制自动管理堆内存,...
### 堆和栈总结 #### 一、堆栈概念概览 在计算机科学中,堆和栈是非常重要的数据结构,它们在...通过熟悉栈帧的结构、局部变量区的特点以及操作数栈的工作方式,开发者能够更好地优化代码,提高程序的效率和稳定性。
在Java程序执行过程中,每当一个方法被调用时,都会在虚拟机栈中创建一个对应的栈帧,用于存储局部变量、操作数、动态链接、方法出口等信息。这个过程与方法的调用和返回紧密相关,栈帧的入栈和出栈顺序反映了方法...
栈则常用于函数调用,每次函数调用都会创建一个新的栈帧,保存局部变量和返回地址,当函数返回时,对应的栈帧会被弹出。 总的来说,理解和熟练运用Java中的队列、链表和栈对于编写高效的代码至关重要。通过深入学习...
栈的主要任务是为每个方法的执行提供一个栈帧(Stack Frame),栈帧存储了方法执行过程中的局部变量、操作数栈、动态连接和方法返回地址等关键信息。 栈帧是虚拟机栈的元素,它包含了以下几个重要组成部分: 1. ...
栈内存主要负责存储程序运行过程中的局部变量,包括基本类型变量(如int、char)和对象的引用变量。在Java中,当函数执行时,这些变量会被分配到栈内存中。栈内存具有快速存取的特点,因为它的数据结构类似于一个...
1. **局部变量表** (Local Variables):局部变量表是一个用于存储方法参数和局部变量的数字数组。数组中的每个元素被称为变量槽(Slot)。局部变量表的大小在编译时期就已经确定,并记录在方法的Code属性中。变量槽...
- **函数调用**:每个函数调用都会在内存中创建一个栈帧,存储局部变量和返回地址,当函数执行完毕,对应的栈帧会被弹出。 4. **自定义栈实现** 虽然`java.util.Stack`提供了栈的功能,但有时我们可能需要更灵活...