简介

咱们晓得JVM运行时数据区域专门有一个叫做Stack Area的区域,专门用来负责线程的执行调用。那么JVM中的栈到底是怎么工作的呢?快来一起看看吧。

JVM中的栈

小师妹:F师兄,JVM为每个线程的运行都调配了一个栈,这个栈到底是怎么工作的呢?

小师妹,咱们先看下JVM的整体运行架构图:

咱们能够看到运行时数据区域分为5大部分。

堆区是存储共享对象的中央,而栈区是存储线程公有对象的中央。

因为是栈的构造,所以这个区域总是LIFO(Last in first out)。咱们思考一个办法的执行,当办法执行的时候,就会在Stack Area中创立一个block,这个block中持有对本地对象和其余对象的援用。一旦办法执行结束,则这个block就会出栈,供其余办法拜访。

Frame

JVM中的stack area是由一个个的Frame组成的。

Frame次要用来存储数据和局部后果,以及执行动静链接,办法的返回值和调度异样。

每次调用办法时都会创立一个新Frame。当Frame的办法调用实现时,无论该办法是失常完结还是异样完结(它引发未捕捉的异样),这个frame都会被销毁。

Frame是从JVM中的stack area中调配的。

每个frame都由三局部组成,别离是本人的local variables数组,本人的operand stack,以及对以后办法的run-time constant pool的援用。

在线程的执行过程中,任何一个时刻都只有一个frame处于活动状态。这个frame被称为current frame,它的办法被称为current 办法,定义以后办法的类是以后类。

如果frame中的办法调用另一个办法或该frame的办法完结,那么这个frame将不再是current frame。

每次调用新的办法,都会创立一个新的frame,并将控制权转移到调用新的办法生成的框架。

在办法返回时,以后frame将其办法调用的后果(如果有的话)传回上一个frame,并完结以后frame。

请留神,由线程创立的frame只能有该线程拜访,并且不能被任何其余线程援用。

Local Variables本地变量

每个frame都蕴含一个称为其本地局部变量的变量数组。frame的局部变量数组的长度是在编译的时候确定的。

单个局部变量能够保留以下类型的值:boolean, byte, char, short, int, float, reference, 或者 returnAddress。

如果对于long或double类型的值须要应用一对局部变量来存储。

局部变量因为存储在数组中,所以间接通过数字的索引来定位和拜访。

留神,这个数组的索引值是从0开始,到数组长度-1完结。

单个局部变量间接通过索引来拜访就够了,那么对于占用两个间断局部变量的long或者double类型来说,怎么拜访呢?

比如说一个long类型占用数组中的n和n+1两个变量,那么咱们能够通过索引n值来拜访这个long类型,而不是通过n+1来拜访。

留神,在JVM中,并不一定要求这个n是偶数。

那么这些局部变量有什么用呢?

Java虚拟机应用局部变量在办法调用时传递参数。

咱们晓得在java中有两种办法,一种是类办法,一种是实例办法。

在类办法调用中,所有参数都从局部变量0开始在间断的局部变量中传递。

在实例办法调用中,局部变量0始终指向的是该实例对象,也就是this。也就是说实在的参数是从局部变量1开始存储的。

Operand Stacks

在每个frame外部,又蕴含了一个LIFO的栈,这个栈叫做Operand Stack。

刚开始创立的时候,这个Operand Stack是空的。而后JVM将local variables中的常量或者值加载到Operand Stack中去。

而后Java虚拟机指令从操作数堆栈中获取操作数,对其进行操作,而后将后果压回操作数堆栈。

比如说,当初的Operand Stack中曾经有两个值,1和2。

这个时候JVM要执行一个iadd指令,将1和2相加。那么就会先将stack中的1和2两个数取出,相加后,将后果3再压入stack。

最终stack中保留的是iadd的后果3。

留神,在Local Variables本地变量中咱们提到,如果是long或者double类型的话,须要两个本地变量来存储。而在Operand Stack中,一个值能够示意任何Java虚拟机类型的值。也就是说long和double在Operand Stack中,应用一个值就能够示意了。

Operand Stack中的任何操作都必须要确保其类型匹配。像之前提到的iadd指令是对两个int进行相加,如果这个时候你的Operand Stacks中存储的是long值,那么iadd指令是会失败的。

在任何工夫点,操作数堆栈都具备关联的深度,其中long或double类型的值对该深度奉献两个单位,而任何其余类型的值则奉献一个单位深度。

Dynamic Linking动静链接

什么是动静链接呢?

咱们晓得在class文件中除了蕴含类的版本、字段、办法、接口
等形容信息外,还有一项信息就是常量池(constant pool table),用于寄存编译器生成的各种字面量(Literal)和符号援用(Symbolic References)。

所谓字面量就是常说的常量,能够有三种形式,别离是:文本字符串,八种根本类型和final类型的常量。

而符号援用是指用符号来形容所援用的指标。

符号援用和间接援用有什么区别呢? 咱们举个例子。

比方咱们定义了String name="jack", 其中jack是一个字面量,会在字符串常量池(String Pool)中保留一份。

如果咱们存储的时候,存的是name,那么这个就是符号援用。

如果咱们存储的是jack在字符串常量池中地址,那么这个就是间接援用。

从下面的介绍咱们能够晓得,为了实现最终的程序失常运行,所有的符号援用都须要转换成为间接援用能力失常执行。

而这个转换的过程,就叫做动静链接。

动静链接将这些符号办法援用转换为具体的办法援用,依据须要加载类以解析尚未定义的符号,并将变量拜访转换为与这些变量的运行时地位关联的存储构造中的适当偏移量。

办法执行结束

办法执行结束有两种模式,一种是失常执行结束,一种是执行过程中抛出了异样。

失常执行结束的办法能够值返回给调用方。

这种状况下frame的作用就是复原调用程序的状态,包含其局部变量和操作数堆栈,并适当减少调用程序的程序计数器以跳过办法调用指令。

如果办法中抛出了异样,那么该办法将不会有值返回给调用方。

本文已收录于:http://www.flydean.com/jvm-thread-stack-frames/

最艰深的解读,最粗浅的干货,最简洁的教程,泛滥你不晓得的小技巧等你来发现!

欢送关注我的公众号:「程序那些事」,懂技术,更懂你!