关于 JVM(Java 虚拟机)来说,它有两个非常重要的区域,一个是栈(Java 虚拟机栈),另一个是堆。堆是 JVM 的存储单位,所有的目标和数组都是存储在此区域的;而栈是 JVM 的运转单位,它主管 Java 程序运转的。那么为什么它有这样的魔力?它存储的又是什么数据?接下来,咱们一同来看。

1.栈界说

咱们先来看栈的界说,咱们这里的栈指的是 Java 虚拟机栈(Java Virtual Machine Stack)也叫做 JVM 栈,《Java虚拟机标准》对此区域的阐明如下:

Each Java Virtual Machine thread has a private Java Virtual Machine stack, created at the same time as the thread. A Java Virtual Machine stack stores frames (2.6). A Java Virtual Machine stack is analogous to the stack of a conventional language such as C: it holds local variables and partial results, and plays a part in method invocation and return. Because the Java Virtual Machine stack is never manipulated directly except to push and pop frames, frames may be heap allocated. The memory for a Java Virtual Machine stack does not need to be contiguous. In the First Edition of The Java Virtual Machine Specification, the Java Virtual Machine stack was known as the Java stack. This specification permits Java Virtual Machine stacks either to be of a fixed size or to dynamically expand and contract as required by the computation. If the Java Virtual Machine stacks are of a fixed size, the size of each Java Virtual Machine stack may be chosen independently when that stack is created. A Java Virtual Machine implementation may provide the programmer or the user control over the initial size of Java Virtual Machine stacks, as well as, in the case of dynamically expanding or contracting Java Virtual Machine stacks, control over the maximum and minimum sizes. The following exceptional conditions are associated with Java Virtual Machine stacks:

  • If the computation in a thread requires a larger Java Virtual Machine stack than is permitted, the Java Virtual Machine throws a StackOverflowError.
  • If Java Virtual Machine stacks can be dynamically expanded, and expansion is attempted but insufficient memory can be made available to effect the expansion, or if insufficient memory can be made available to create the initial Java Virtual Machine stack for a new thread, the Java Virtual Machine throws an OutOfMemoryError.

以上内容翻译成中文的含义如下: Java 虚拟机栈是线程私有的区域,它跟着线程的创立而创立。它里边保存的是局部变量表(基础数据类型和目标引证地址)和计算进程中的中心成果。Java 虚拟机的内存不需要连续,它只有两个操作:入栈和出栈。

Java 虚拟机栈要么大小固定,要么根据计算动态的扩展和缩短。程序员可以对 Java 虚拟机栈进行初始值的大小设置和最大值的设置。

Java 虚拟机栈呈现的反常有两种:

  • 当 Java 虚拟机栈大小固定时,假如程序中的栈分配超过了最大虚拟机栈就会呈现 StackOverflowError 反常。

  • 假如 Java 虚拟机栈是动态扩展的,那么当内存不足时,就会引发 OutOfMemoryError 的反常。

    2.栈结构

    栈是线程私有的,每个线程都有自己的栈(空间),栈中的数据是以栈帧(Stack Frame)的形式存在的,线程会为每个正在履行的办法生成一个栈帧,如下图所示:

    对线面试官:浅聊一下 Java 虚拟机栈?

    PS:当一个新的办法被调用时,就会在栈中创立一个栈帧,当办法调用完成之后,也就意味着这个栈帧会履行出栈操作。

而栈帧中又存储了 5 个内容:

  1. 局部变量表(Local Variables);
  2. 操作(数)栈(Operand Stack);
  3. 动态链接(Dynamic Linking);
  4. 办法回来地址(Return Address);
  5. 附加信息。

如下图所示:

对线面试官:浅聊一下 Java 虚拟机栈?
栈的全体存储结构如下图所示:
对线面试官:浅聊一下 Java 虚拟机栈?

2.1 局部变量表

局部变量表也叫做局部变量数组或本地变量表。 局部变量表是一个数组,里边存储的内容有:

  • 办法参数;
  • 办法内的局部变量,也便是办法内的根本数据类型和目标引证(Reference);
  • 办法回来类型(Return Address)。

接下来咱们经过类生成的字节码来观察一下局部变量表的内容,首要,咱们先来搞一个 main 办法,详细代码如下:

public static void main(String[] args) {
    int num = 0;
    LocalVariablesExample lv =
            new LocalVariablesExample();
}

然后咱们编译类,再运用“javap -v LocalVariablesExample.class”查看字节码生成的内容,其间包括的本地变量表内容如下:

对线面试官:浅聊一下 Java 虚拟机栈?
咱们经过 JClassLib 也能观察到局部变量表的信息,如下图所示为局部变量表的长度:
对线面试官:浅聊一下 Java 虚拟机栈?
局部变量表的详细信息如下:
对线面试官:浅聊一下 Java 虚拟机栈?

2.2 操作栈

操作栈也叫做操作数栈或表明式栈,操作数栈首要用于保存计算进程的中心成果,一起作为计算进程中变量临时的存储空间。

考虑:为什么不把程序履行进程中的中心成果保存到局部变量表,而是保存到操作数栈中呢?

因为局部变量表是数组,而数组的长度是在其创立时就要确定,所以局部变量表在编译器就决定内容和大小了,那么在程序履行中的这些动态中心成果,是需要新的空间来保存了,而操作数栈就可以完成此功用。

2.3 动态链接

动态链接也叫做指向运转时常量池的办法引证。

这个区域的概念和效果略微难了解一点,在每一个栈帧内部都包括一个指向运转时常量池中该栈帧所属办法的引证。当一个办法调用了别的的其他办法时,便是经过常量池中指向办法的符号引证来表明的,那么动态链接的效果便是为了将这些符号引证转换为调用办法的直接引证。

也便是说:当一个办法调用另一个办法时,不会再创立一个被调用的办法,而是经过常量池的办法引证来调用,而这个区域存储的便是运转时常量池的办法引证,这个区域的效果便是将运转时常量池的符号引证转换成直接引证。

2.4 办法回来地址

办法回来地址也叫做办法正常退出或反常退出的界说。

办法回来地址寄存的是调用该办法的程序计数器的值。程序计数器里边保存的是该线程要履行的下一行指令的方位

也便是说:在一个办法中调用了另一个办法,当被调用的办法履行完之后,要履行的下一行指令便是保存在此区域的。

2.5 附加信息

此区域在很多教程上会被省掉,因为此区域有可能有数据,也有可能没有数据。这些附加信息是和 Java 虚拟机完成相关的一些信息。例如,对程序调试提供支撑的信息。

总结

栈作为 Java 虚拟机中最核心的组成部分之一,它包括了以下 5 部分的内容:

  1. 局部变量表(Local Variables):首要存储的是办法内的根本数据类型和目标引证;
  2. 操作(数)栈(Operand Stack):首要用于保存计算进程的中心成果,一起作为计算进程中变量临时的存储空间;
  3. 动态链接(Dynamic Linking):寄存的是指向运转时常量池的办法引证;
  4. 办法回来地址(Return Address):寄存的是调用该办法的程序计数器的值;
  5. 一些附加信息:存储了一些和 Java 虚拟相关的数据,比方程序的调试数据。

参阅 & 鸣谢

《阿里巴巴Java开发手册》 《尚硅谷JVM》

本文已收录到 Gitee 开源库房《Java 面试指南》,其间包括的内容有:Redis、JVM、并发、并发、MySQL、Spring、Spring MVC、Spring Boot、Spring Cloud、MyBatis、规划模式、消息队列等模块。Java 面试有它就够了:超全 Java 常见面试题,继续更新…