Java栈概述
记得当初我学习java时,常常听见身边的朋友说:“你要记住,当new一个对象时,对象的引用存放在栈里,而对象是存放在堆里的”。当时,听到这句教导时,脑海里立即出现栈的模型——里面存的仅仅是个引用。最近,看了下《深入JVM》,才发现,原来栈并不是我想象的那么简单,它和我想象中的那个栈的结构差别非常大。
每当启用一个线程时,JVM就为他分配一个JAVA栈,栈是以帧为单位保存当前线程的运行状态。某个线程正在执行的方法称为当前方法,当前方法使用的栈帧称为当前帧,当前方法所属的类称为当前类,当前类的常量池称为当前常量池。当线程执行一个方法时,它会跟踪当前常量池。
每当线程调用一个java方法时,JVM就会在该线程对应的栈中压入一个帧,这个帧自然就成了当前帧。当执行这个方法时,它使用这个帧来存储参数、局部变量、中间运算结果等等。
java栈上的所有数据都是私有的。任何线程都不能访问另一个线程的栈数据。所以我们不用考虑多线程情况下栈数据访问同步的情况。
像方法区和堆一样,java栈和帧在内存中也不必是连续的,帧可以分布在连续的栈里,也可以分布在堆里
java栈的组成元素——栈帧
栈帧由三部分组成:局部变量区、操作数栈、帧数据区。局部变量区和操作数栈的大小要视对应的方法而定,他们是按字长计算的。但调用一个方法时,它从类型信息中得到此方法局部变量区和操作数栈大小,并据此分配栈内存,然后压入Java栈。
局部变量区 局部变量区被组织为以一个字长为单位、从0开始计数的数组,类型为short、byte和char的值在存入数组前要被转换成int值,而long和double在数组中占据连续的两项,在访问局部变量中的long或double时,只需取出连续两项的第一项的索引值即可,如某个long值在局部变量区中占据的索引时3、4项,取值时,指令只需取索引为3的long值即可。
说再多也没用,下面就看个例子,好让大家对局部变量区有更深刻的认识。这个图来着《深入JVM》:
- public static int runClassMethod(int i,long l,float f,double d,Object o,byte b) {
- return 0;
- }
- public int runInstanceMethod(char c,double d,short s,boolean b) {
- return 0;
- }
上面代码片的方法参数和局部变量在局部变量区中的存储结构如下图:
上面这个图没什么好说的,大家看看就会懂。但是,在这个图里,有一点需要注意:
runInstanceMethod的局部变量区第一项是个reference(引用),它指定的就是对象本身的引用,也就是我们常用的this,但是在runClassMethod方法中,没这个引用,那是因为runClassMethod是个静态方法。
操作数栈 和局部变量区一样,操作数栈也被组织成一个以字长为单位的数组。但和前者不同的是,它不是通过索引来访问的,而是通过入栈和出栈来访问的。可把操作数栈理解为存储计算时,临时数据的存储区域。下面我们通过一段简短的程序片段外加一幅图片来了解下操作数栈的作用。
int a = 100;
int b = 98;
int c = a+b;
从图中可以得出:操作数栈其实就是个临时数据存储区域,它是通过入栈和出栈来进行操作的。
帧数据区 除了局部变量区和操作数栈外,java栈帧还需要一些数据来支持常量池解析、正常方法返回以及异常派发机制。这些数据都保存在java栈帧的帧数据区中。
当JVM执行到需要常量池数据的指令时,它都会通过帧数据区中指向常量池的指针来访问它。
除了处理常量池解析外,帧里的数据还要处理java方法的正常结束和异常终止。如果是通过return正常结束,则当前栈帧从Java栈中弹出,恢复发起调用的方法的栈。如果方法又返回值,JVM会把返回值压入到发起调用方法的操作数栈。
为了处理java方法中的异常情况,帧数据区还必须保存一个对此方法异常引用表的引用。当异常抛出时,JVM给catch块中的代码。如果没发现,方法立即终止,然后JVM用帧区数据的信息恢复发起调用的方法的帧。然后再发起调用方法的上下文重新抛出同样的异常。
栈的整个结构
在前面就描述过:栈是由栈帧组成,每当线程调用一个java方法时,JVM就会在该线程对应的栈中压入一个帧,而帧是由局部变量区、操作数栈和帧数据区组成。那在一个代码块中,栈到底是什么形式呢?下面是我从《深入JVM》中摘抄的一个例子,大家可以看看:
代码片段:
执行过程中的三个快照:
上面所给的图,只想说明两件事情:
1. 只有在调用一个方法时,才为当前栈分配一个帧,然后将该帧压入栈
2. 帧中存储了对应方法的局部数据,方法执行完,对应的帧则从栈中弹出,并把返回结果存储在调用 方法的帧的操作数栈中
相关推荐
在类中的位置不同 成员变量:在类中方法外 局部变量:在方法内或者方法声明处 在内存中的位置不同 成员变量:在堆内存中 局部变量:在栈内存中 ... 成员变量和局部变量的名称可以一致,方法调用的时候采取就近原则
2. **存储位置**:局部变量一般存储在栈中。 3. **初始化**:如果未显式初始化,局部变量的值将是不确定的,除非使用`static`关键字进行初始化。 4. **可见性**:局部变量只在其所在函数或代码块内可见。 #### 使用...
- 当函数被调用时,编译器会为该函数创建一个新的栈帧(stack frame),用来存储局部变量和函数参数。 - 函数执行过程中,局部变量会被压入栈中。 - 函数执行完毕后,栈帧被销毁,局部变量随之释放。 - 这种后进先出的...
这类变量包括静态局部变量和全局变量,它们存储在静态数据区中。静态存储变量的生命周期与程序的生命周期相同,从程序启动到程序结束。 #### 五、静态局部变量 - **存储位置**:静态局部变量存储在静态数据区。 - ...
本文将详细介绍Java虚拟机(JVM)的内部机理和实现原理,从类型的生命周期、方法区、常量池、类加载器、垃圾收集器、栈和局部变量等方面对JVM进行深入解析。 类型的生命周期 类型的生命周期是JVM中最重要的部分,...
### 局部变量能否和全局变量重名 在编程中,局部变量与全局变量的作用域是不同的,这决定了它们在程序中的使用方式及其可见性。根据题目中的描述,我们可以了解到以下知识点: #### 1. 局部变量与全局变量重名的...
- **局部变量**:局部变量在栈中分配空间,随函数调用的开始而创建,函数调用结束时销毁。 ### 面向对象的`static`关键字 在面向对象编程中,`static`关键字用于定义静态成员变量和静态成员函数。 #### 静态数据...
由于每个线程都有自己的独立的调用栈,因此,从理论上来讲,局部变量在多线程环境中是线程安全的,因为每个线程都拥有自己独立的一份副本。 然而,实际的情况可能更为复杂。例如,如果局部变量引用了非线程安全的...
变量类型和内存管理 变量类型是编程语言中非常重要的一个概念,...我们可以看到,全局变量、静态变量、局部变量和堆区中分配的变量都是程序中常用的变量类型,它们之间的区别和联系都是程序员需要了解的重要知识点。
- 当设计和使用访问动态全局变量、静态全局变量、静态局部变量的函数时,需考虑重入问题。 - 为了创建可重入的函数,应避免在函数中使用`static`变量。 - **特殊用途**: - 当函数返回值为指针类型时,通常使用...
按照存储区域来划分,全局变量、静态全局变量和静态局部变量都存放在内存的全局数据区(也称为静态存储区),而局部变量则存放在内存的栈区。 1. **全局变量**:在程序的整个执行过程中始终存在,即使在函数调用...
- **局部变量**一般存储在栈中,随函数调用的开始而创建,随函数调用的结束而销毁。 - **静态局部变量**虽然在函数内部定义,但存储在静态存储区中,整个程序运行期间都存在。 #### 四、面向过程设计中的static...
2. **存储位置**:`static`静态局部变量存储在程序的数据段中,而非栈中。 3. **生命周期**:从程序启动到结束,`static`静态局部变量一直存在。 #### 六、总结 通过上述示例可以看出,合理使用`static`静态局部...
在《JVM指令手册》中,主要涵盖了栈和局部变量的操作,这是理解JVM工作原理的关键部分。 1. 栈和局部变量操作: JVM使用栈来存储临时计算结果,局部变量表则是存储方法参数和局部变量的地方。以下是一些主要的指令...
下面我们将对局部变量和全局变量的区别进行详细的分析,并说明栈、堆和静态存储区的区别。 一、局部变量和全局变量的区别 局部变量是指在函数体内定义的变量,它们只在函数体内有效,函数调用结束后,变量就会被...
在编程语言中,如C语言,变量是存储数据的基本单元,它们可以分为两类:局部变量和全局变量。局部变量在函数内部定义,只在该函数内部有效,而全局变量在整个程序范围内都可见。理解这两者的区别和作用域对编写高效...
一、局部变量和全局变量 局部变量可以与全局变量同名,在函数内引用这个变量时,会用到同名的局部变量,而不会用到全局变量。在同一个函数内可以定义多个同名的局部变量,每个局部变量的作用域就在那个循环体内。 ...
首先,JVM指令手册中提到的操作栈和局部变量的指令分为两大类:常量压栈指令和局部变量加载指令。常量压栈指令用于将各种类型的常量值(null, int, long, float, double, char, byte, short)压入操作数栈。例如,a...
每个进程都有一个调用栈(call stack),其中每个未完成的函数都对应一个栈帧(stack frame),栈帧包含局部变量和传递给被调函数的参数。栈从高地址开始,向低地址扩展,栈底存储高地址,栈顶存储低地址。 寄存器...
全局变量和局部变量是编程语言中两种不同的变量类型,它们在C++中有着明确的差异,主要体现在作用域、内存分配、生命周期以及对外链接性四个方面。 1. **作用域(Scope)** - **全局变量**:全局变量在整个程序...