程序计数器和虚拟机栈

程序计数器

程序计数器是一块较小的内存空间,它的作用可以看作是当前线程所执行的字节码的行号指示器。在虚拟机的概念模型里字节码解释器工作时就是通过改变这个计数器的值来选取下一条需要执行的字节码指令,分支、循环、跳转、异常处理、线程恢复等基础功能都需要依赖这个计数器来完成。- -《深入理解Java虚拟机》

特点:

如果线程正在执行的是Java 方法,则这个计数器记录的是正在执行的虚拟机字节码指令地址

如果正在执行的是Native 方法,则这个技术器值为空(Undefined)

此内存区域是唯一一个在Java虚拟机规范中没有规定任何OutOfMemoryError情况的区域

为什么执行的是native 方法时,为undefined

由上我们知道计数器记录的字节码指令地址,但是native 本地(如:System.currentTimeMillis()/ public static native long currentTimeMillis();)方法是大多是通过C实现并未编译成需要执行的字节码指令所以在计数器中当然是空(undefined).

虚拟机栈

特点:

1. Java虚拟机栈也是线程私有的,它的生命周期与线程相同(随线程而生,随线程而灭)

    2. 如果线程请求的栈深度大于虚拟机所允许的深度,将抛出StackOverflowError异常;

3.对于执行引擎来说,活动线程中,只有栈顶的栈帧是有效的,

如果虚拟机栈可以动态扩展,如果扩展时无法申请到足够的内存,就会抛出             OutOfMemoryError异常;

Exception in thread "main" java.lang.StackOverflowError


3. Java虚拟机栈描述的是Java方法执行的内存模型:每个方法执行的同时会创建一个栈帧。

栈帧(stack frame)

栈帧(Stack Frame)是用于支持虚拟机进行方法调用和方法执行的数据结构。它是虚拟机运行时数据区中的java虚拟机栈的栈元素。

栈帧存储了方法的局部变量表、操作数栈、动态连接和方法返回地址等信息。

每一个方法从调用开始至执行完成的过程,都对应着一个栈帧在虚拟机里面从入栈到出栈的过程。

栈结构图


局部变量表(Local Variable Table)是一组变量值存储空间,用于存放方法参数和方法内部定义的局部变量。

并且在Java编译为Class文件时,就已经确定了该方法所需要分配的局部变量表的最大容量。但是,如果是成员变量,或者定义在方法外对象的引用,它们存储在堆中。因为在堆中,是线程共享数据的。

局部变量表的容量以变量槽(Slot)为最小单位,每个变量槽都可以存储32位长度的内存空间,例如boolean、byte、char、short、int、float、reference。

对于64位长度的数据类型(long,double),虚拟机会以高位对齐方式为其分配两个连续的Slot空间,也就是相当于把一次long和double数据类型读写分割成为两次32位读写。

操作数栈(operand stack)

与局部变量表一样,均以字长为单位的数组。不过局部变量表用的是索引,操作数栈是弹栈/压栈来访问。操作数栈可理解为java虚拟机栈中的一个用于计算的临时数据存储区。

虚拟机把操作数栈作为它的工作区——大多数指令都要从这里弹出数据,执行运算,然后把结果压回操作数栈。比如,iadd指令就要从操作数栈中弹出两个整数,执行加法运算,其结果又压回到操作数栈中

Java代码

begin  

iload_0    // push the int in local variable 0 onto the stack  

iload_1    // push the int in local variable 1 onto the stack  

iadd       // pop two ints, add them, push result  

istore_2   // pop int, store into local variable 2  

end  

在这个字节码序列里,前两个指令iload_0和iload_1将存储在局部变量中索引为0和1的整数压入操作数栈中,其后iadd指令从操作数栈中弹出那两个整数相加,再将结果压入操作数栈。第四条指令istore_2则从操作数栈中弹出结果,并把它存储到局部变量区索引为2的位置。下图详细表述了这个过程中局部变量和操作数栈的状态变化,图中没有使用的局部变量区和操作数栈区域以空白表示。

操作数栈

你可能感兴趣的:(程序计数器和虚拟机栈)