Java栈也称作虚拟机栈(Java Vitual Machine Stack)

每个Java虚拟机线程都有一个私有Java虚拟机栈,与该线程同时创建。Java虚拟机栈存储栈帧。

这里解释一下什么是栈帧:栈帧是在方法被调用时创建的,方法调用完成后销毁。栈帧内存放着方法中的局部变量,操作数栈、运行时常量池的引用等数据

栈内存包含了局部变量和方法调用。JVM栈只对栈帧进行存储,压栈和出栈的操作。Java栈是Java方法执行的内存模型。下面我们来看一个Java栈图。 https://img-blog.csdn.net/20170301172402015?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvemhhbmdxaWx1R3J1YmJ5/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast 从上图可以看出来,栈中存放着一个个栈帧,当线程执行一个方法时,该线程就会创建一个栈帧,并用创建的栈帧进行压栈,方法执行完毕后,便将栈帧出栈。由此可知道,线程执行的方法对应的栈帧必定处于栈顶

JVM规范允许Java虚拟机堆栈具有固定的大小,或者根据计算的需要动态扩展和收缩。

如果Java虚拟机堆栈的大小是固定的,则每个Java虚拟机堆栈的大小可以在创建堆栈时独立选择。

Java虚拟机实现可以为程序员或用户提供对Java虚拟机栈初始大小的控制,并且在动态扩展或收缩Java虚拟机堆栈的情况下,可以控制最大和最小大小。

  • 如果线程中的计算需要比允许的更大的Java虚拟机栈,那么Java虚拟机将抛出a StackOverflowError。

  • 如果Java虚拟机堆栈可以动态扩展,并且尝试扩展,但是没有足够的内存可用于扩展,或者如果没有足够的内存可用于为新线程创建初始Java虚拟机堆栈,则Java虚拟机机器抛出一个OutOfMemoryError。

深入理解栈 -- 栈帧

栈帧由三部分组成:局部变量区、操作数栈、帧数据区。 局部变量区和操作数栈的大小要视对应的方法而定,他们是按字长计算的。但调用一个方法时,它从类型信息中得到此方法局部变量区和操作数栈大小,并据此分配栈内存,然后压入Java栈。

局部变量区

局部变量区存放着基本数据类型对象和其他引用型对象的引用,实际的引用型对象存放于堆内存中。 局部变量区被组织为以一个字长为单位、从0开始计数的数组,类型为short、byte和char的值在存入数组前要被转换成int值,而long和double在数组中占据连续的两项,在访问局部变量中的long或double时,只需取出连续两项的第一项的索引值即可,如某个long值在局部变量区中占据的索引时3、4项,取值时,指令只需取索引为3的long值即可。

操作数栈

和局部变量区一样,操作数栈也被组织成一个以字长为单位的数组。但和前者不同的是,它不是通过索引来访问的,而是通过入栈和出栈来访问的。可把操作数栈理解为存储计算时,临时数据的存储区域。

帧数据区

除了局部变量区和操作数栈外,java栈帧还需要一些数据来支持常量池解析、正常方法返回以及异常派发机制。这些数据都保存在java栈帧的帧数据区中。

当JVM执行到需要常量池数据的指令时,它都会通过帧数据区中指向常量池的指针来访问它。

除了处理常量池解析外,帧里的数据还要处理java方法的正常结束和异常终止。如果是通过return正常结束,则当前栈帧从Java栈中弹出,恢复发起调用的方法的栈。如果方法又返回值,JVM会把返回值压入到发起调用方法的操作数栈。

为了处理java方法中的异常情况,帧数据区还必须保存一个对此方法异常引用表的引用。当异常抛出时,JVM给catch块中的代码。如果没发现,方法立即终止,然后JVM用帧区数据的信息恢复发起调用的方法的帧。然后再发起调用方法的上下文重新抛出同样的异常。

总结

  • 栈随线程的创建而创建,内存是私有的
  • 栈中存储着的是栈帧,栈帧由三部分组成:局部变量区、操作数栈、帧数据区。
  • 局部变量区存放着基本数据类型对象和其他引用型对象的引用
  • 栈内存空间可以设置固定大小或者设置成动态扩展或缩小
    • 如果线程需要栈空间超过了栈空间分配的内存,则会抛出一个StackOverflowError
    • 如果栈空间不足,并尝试扩展,但是没有足够的内存可用于扩展,或者如果没有足够的内存可用于为新线程创建初始栈内存,则会抛出一个OutOfMemoryError。

results matching ""

    No results matching ""