原文应该也是在JavaEye中的博客,找不到了。
在原文基础了做了增删,以方便理解。
Java中的栈
每当线程调用一个Java方法时,JVM就会在该线程对应的栈中压入一个帧,这个帧自然就成了当前帧。当执行这个方法时,它使用这个帧来存储参数、局部变量、中间运算结果等等。
Java栈上的所有数据都是私有的。任何线程都不能访问另一个线程的栈数据。所以我们不用考虑多线程情况下栈数据访问同步的情况。
像方法区和堆一样,Java栈和帧在内存中也不必是连续的,帧可以分布在连续的栈里,也可以分布在堆里
Java栈的组成元素——栈帧
栈帧由三部分组成:局部变量区、操作数栈、帧数据区。局部变量区和操作数栈的大小要视对应的方法而定,他们是按字长计算的。但调用一个方法时,它从类型信息中得到此方法局部变量区和操作数栈大小,并据此分配栈内存,然后压入Java栈。
局部变量区 局部变量区被组织为以一个字长为单位、从0开始计数的数组,类型为short、byte和char的值在存入数组前要被转换成int值,而long和double在数组中占据连续的两项,在访问局部变量中的long或double时,只需取出连续两项的第一项的索引值即可,如某个long值在局部变量区中占据的索引时3、4项,取值时,指令只需取索引为3的long值即可。
Each frame (§3.6) contains an array of variables known as its local variables. The length of the local variable array of a frame is determined at compile time and supplied in the binary representation of a class or interface along with the code for the method associated with the frame (§4.7.3).
下面就看个例子,好让大家对局部变量区有更深刻的认识。这个图来自《深入JVM》:
- public static int runClassMethod(inti,longl,floatf,doubled,Objecto,byteb){
- return0;
- }
- public int runInstanceMethod(charc,doubled,shorts,booleanb){
- return0;
- }
上面代码片的方法参数和局部变量在局部变量区中的存储结构如下图:
上面这个图没什么好说的,大家看看就会懂。但是,在这个图里,有一点需要注意:
runInstanceMethod的局部变量区第一项是个reference(引用),它指定的就是对象本身的引用,也就是我们常用的this,但是在runClassMethod方法中,没这个引用,那是因为runClassMethod是个静态方法。
操作数栈和局部变量区一样,操作数栈也被组织成一个以字长为单位的数组。但和前者不同的是,它不是通过索引来访问的,而是通过入栈和出栈来访问的。可把操作数栈理解为存储计算时,临时数据的存储区域。下面我们通过一段简短的程序片段外加一幅图片来了解下操作数栈的作用,以及操作数栈与本地变量区的状态变化。
int a = 100;
int b = 98;
int c = a+b;
字节码序列:
iload_0//入栈
iload_1//入栈
iadd//弹出2个栈中数据相加后,把结果入栈
istore_2 //弹出结果存储到局部变量中位置2处
从图中可以得出:操作数栈其实就是个临时数据存储区域,它是通过入栈和出栈来进行操作的。
帧数据区除了局部变量区和操作数栈外,Java栈帧还需要一些数据来支持常量池解析、正常方法返回以及异常派发机制。这些数据都保存在Java栈帧的帧数据区中。
当JVM执行到需要常量池数据的指令时,它都会通过帧数据区中指向常量池的指针来访问它。
除了处理常量池解析外,帧里的数据还要处理Java方法的正常结束和异常终止。如果是通过return正常结束,则当前栈帧从Java栈中弹出,恢复发起调用的方法的栈。如果方法又返回值,JVM会把返回值压入到发起调用方法的操作数栈。
为了处理Java方法中的异常情况,帧数据区还必须保存一个对此方法异常引用表的引用。当异常抛出时,JVM给catch块中的代码。如果没发现,方法立即终止,然后JVM用帧区数据的信息恢复发起调用的方法的帧。然后再发起调用方法的上下文重新抛出同样的异常。
栈的整个结构
在前面就描述过:栈是由栈帧组成,每当线程调用一个Java方法时,JVM就会在该线程对应的栈中压入一个帧,而帧是由局部变量区、操作数栈和帧数据区组成。那在一个代码块中,栈到底是什么形式呢?下面是我从《深入JVM》中摘抄的一个例子,大家可以看看:
代码片段:
public class JVMStack { public static void addAndPrint() { double result = addTwoTypes(1, 88.88); System.out.println(result); } public static double addTwoTypes(int i, double d) { return i + d; } }该类对应的ByteCode,使用eclipse bytecode outline插件查看
public static addAndPrint()V L0 ICONST_1 //常量入参1入栈 push 1 onto the stack LDC 88.88 //88.88入栈 INVOKESTATIC com/alipay/test/JVMStack.addTwoTypes(ID)D //调用方法addTwoTypes DSTORE 0 //将结果出栈并保存到本地变量数组 L1 GETSTATIC java/lang/System.out : Ljava/io/PrintStream; DLOAD 0 INVOKEVIRTUAL java/io/PrintStream.println(D)V L2 RETURN public static addTwoTypes(ID)D //新的方法有新的栈帧 L0 ILOAD 0 //第一个入参入栈 I2D //int转换为double DLOAD 1 //第二个参数入栈 DADD //执行double 加法运算 DRETURN //返回 }
执行过程中的三个快照:
上面所给的图,只想说明两件事情,我们也可用此来理解Java中的栈:
1、只有在调用一个方法时,才为当前栈分配一个帧,然后将该帧压入栈。
2、帧中存储了对应方法的局部数据,方法执行完,对应的帧则从栈中弹出,并把返回结果存储在调用方法的帧的操作数栈中。
Questions:
1栈桢由那三部分组成,各自存储了什么数据?
2 操作指令在栈桢中是如何工作的?
3 如果发生零除异常,它是如何被抛出的?
相关推荐
在Java程序执行过程中,JVM会根据方法的调用关系创建和销毁栈桢。 二、栈桢的结构 栈桢的结构可以分为以下几个部分: * 局部变量数组(Local Variables):存储方法的局部变量的值。 * 操作数栈(Operand Stack)...
每个方法从调用直结束就对于一个栈桢在虚拟机栈中的入栈和出栈过程。 本地方法栈(Native Method Stacks) 本地方法栈是 JVM 中的一个线程私有的数据区域,这部分主要与虚拟机用到的 Native 方法相关,一般情况下...
每个时刻正在执行的当前方法就是虚拟机栈顶的栈桢。栈的大小缺省为 1M,可用参数 –Xss 调整大小。 * 局部变量表:顾名思义就是局部变量的表,用于存放我们的局部变量的。首先它是一个 32 位的长度,主要存放我们...
它描述的是 Java 方法执行的内存模型:每个方法被执行的时候都会创建一个栈桢用来存储局部变量表、方法出入口信息等。 3. 本地方法栈:此区域与虚拟机栈起到的作用类似,只不是过虚拟机栈是针对 Java 方法而这个...
虚拟机栈(线程私有) .................................................................................................................... 22 2.2.3. 本地方法区(线程私有) ...................................
文章目录Class 文件格式字节码Class类的本质Class文件格式类加载机制加载验证准备解析初始化类加载器双亲委派机制栈桢JVM方法调用详解方法解析静态分派动态分派参考 Class 文件格式 一般情况下Java代码执行流程如下...
2.2.2. 虚拟机栈(线程私有) .................................................................................................................... 22 2.2.3. 本地方法区(线程私有) .............................
这里,iload_0表示将局部变量表中索引为0的元素的值放到栈顶,iinc 0, 1表示将局部变量表中索引为0的元素进行加1,最后是ireturn将栈顶的int值放入到调用者栈桢的栈中。从这里可以看到,iinc 0, 1并没有实际影响到...
虚拟机栈描述的是Java方法的执行内存模型,每个方法在执行的同时会创建一个栈桢(stack frame)用于存储局部变量表、操作数栈、动态链表、方法出口等信息。虚拟机栈存储的数据类型包括局部变量表、操作数栈、动态...
描述了后端开发的技术栈,可能包括服务器端语言(如Java、Python或Node.js)、数据库管理系统(如MySQL、MongoDB)以及API设计。 四、测试计划: 这部分将规划软件的测试策略,包括单元测试、集成测试、系统测试和...
IoC、AOP加解密:DES、MD5、AESXML/JSON:fastjson、XStreamLog4j:常用的xml配置、输出栈桢的用法Commons BeanUtils:General:比较器、org.json.JSONObject、Google Gson、Alibaba Fastjson、时间戳、IP地址解析...