每个线程都会在栈空间中为其分配一块区域,即线程隔离。
-Xss 每个线程的栈容量大小
栈帧
虚拟机栈中的栈元素
用于支持虚拟机进行方法调用和方法执行的数据结构
每个方法从开始调用到执行完成的过程,就对应着一个栈帧从入栈到出栈的过程。
在活动线程中,只有位于栈顶的栈帧才是有效的,称为当前栈帧,与这个栈帧相关联的方法称为当前方法,执行引擎运行的所有字节码指令都只针对当前栈帧进行操作。
栈帧主要包括:局部变量表 操作数栈 动态连接 方法返回地址 附加信息
局部变量表
存放方法参数和方法内的局部变量
在java程序编译为字节码文件时,就在方法的Code属性的max_locals数据项中确定了需要分配的局部变量表的最大容量。
局部变量表的存储单位是变量槽 Variable Slot,每个槽存放的容量是32字节位,(针对double、long这样的64字节位的基本数据类型, 用两个槽来存放,因此在编译的时候是可以算出max_locals的值)
我们一般说的栈中对象引用就是存到这个栈帧中的局部变量表里的,这里的引用指的是局部变量的对象引用,而不是成员变量的引用。
成员变量的对象引用是存储在堆(heap)中
操作数栈
方法的执行操作在操作数栈中完成,每一个字节码指令往操作数栈进行写入和提取的过程,就是入栈和出栈的过程。
我们所说的栈深度,其实就是指的这个操作数栈的深度,每个32位数据类型占用的栈深度为1,64位为2。
同局部变量表一样,栈的最大深度(max_stacks),在编译的时候就在方法的Code属性max_stacks中确定了要分配的最大深度。
当在方法执行时,操作数栈的深度超过了max_stacks,(比如一直while(true) int a = 1),这时会有两种情况:
如果max_stacks允许扩展,则扩大最大深度,当虚拟机在扩展栈时无法申请到足够的空间,会抛出OutOfMemoryError;
如果不允许,则会抛出StackOverFlowException;
动态连接
每个栈帧都包含一个指向运行时常量池中该栈帧所属方法的引用,持有这个引用是为了支持方法调用过程的动态连接。
静态解析:常量池中的符号引用,一部分会在类加载阶段或第一是时间的时候就转化为直接引用。
动态连接:另一部分在每一个运行期间转化为直接引用,这个过程称作动态连接。
方法返回地址
方法退出的方式分两种:正常完成出口和异常完成出口
正常完成出口:执行引擎执行任意一个方法返回(如:return)的字节码指令,这时候会可能会有返回值返回值方法的调用者。
一般来说,正常退出时,调用者的PC计数器的值可以作为放回地址。
异常完成出口:在方法执行的过程中遇到了异常,且没有在方法体中进行处理。异常完成出口退出时,不会给上层调用者任何返回值。
方法退出实际上等同于当前栈帧出栈,因此一般过程为:
- 恢复上层方法的局部变量表和操作数栈。
- 把返回值压入调用者栈帧的操作数栈中
- 调整PC计数器的值以指向后面一条指令。
附加信息
虚拟机实现的一些不在规范中的信息保存到栈帧中。
一般把动态连接,方法返回地址和附加信息统称为栈帧信息。
以上内容大部分摘自:《深入理解java虚拟机》 -- 周志明