每个线程在创建时都会创建一个虚拟机栈,其内部保存一个个的栈帧(Stack Frame),对应着一次次的 Java 方法调用,是线程私有的。
作用:主管 Java 程序的运行,它保存方法的局部变量、部分结果,并参与方法的调用和返回。
栈中可能出现的异常:

栈运行原理:
在一条活动线程中,一个时间点上,只会有一个活动的栈帧。即只有当前正在执行的方法的栈帧(栈顶栈帧)是有效的,这个栈帧被称为当前栈帧(Current Frame),与当前栈帧相对应的方法就是当前方法(Current Method),定义这个方法的类就是当前类(Current Class)。
执行引擎运行的所有字节码指令只针对当前栈帧进行操作。
如果在该方法中调用了其他方法,对应的新的栈帧会被创建出来,放在栈的顶端,成为新的当前帧。
不同线程中所包含的栈帧是不允许存在相互引用的,即不可能在一个栈帧之中引用另外一个线程的栈帧。
栈帧的内部结构:

局部变量表也被称之为局部变量数组或本地变量表:
数组,主要用于存储方法参数和定义在方法体内的局部变量,这些数据类型包括各类基本数据类型、对象引用(reference),以及 returnAddress 类型。关于 Slot 的理解:
静态变量与局部变量的对比:
我们知道类变量有两次初始化的机会,第一次是在“准备阶段”(链接),执行系统初始化,对类变量设置默认值,另一次则是在“初始化”阶段,赋予程序员在代码中定义的初始值。
和类变量初始化不同的是,局部变量表不存在系统初始化的过程,这意味着一旦定义了局部变量则必须人为的初始化,否则无法使用。
补充说明:
垃圾回收根节点,只要被局部变量表中直接或间接引用的对象都不会被垃圾回收。操作数栈,在方法执行过程中,根据字节码指令,往栈中写入数据或拿出数据,即入栈和 出栈
操作数栈,主要用于保存计算过程的中间结果,同时作为计算过程中变量临时的存储空间。
另外,我们说 Java 虚拟机的解释引擎是基于栈的执行引擎,其中的栈指的就是操作数栈。
每一个栈帧内部都包含一个指向运行时常量池中该栈帧所属方法的引用。(运行时常量池在方法区里面,元空间)包含这个引用的目的就是为了支持当前方法的代码能够实现动态链接(Dynamic Linking)。比如:invokedynamic 指令
在 Java 源文件被编译到字节码文件中时,所有的变量和方法引用都作为符号引用(Symbolic Reference)保存在 class 文件的常量池里。比如:描述一个方法调用了另外的其他方法时,就是通过常量池中指向方法的符号引用来表示的,那么动态链接的作用就是为了将这些符号引用转换为调用方法的直接引用(#------>>>具体名字)。

静态链接:当一个字节码文件被装载进 JVM 内部时,如果被调用的目标方法在编译期可知,且运行期保持不变时,这种情况下降调用方法的符号引用转换为直接引用的过程称之为静态链接
动态链接:如果被调用的方法在编译期无法被确定下来,只能够在程序运行期将调用的方法的符号转换为直接引用,由于这种引用转换过程具备动态性,因此也被称之为动态链接。
对应的方法的绑定机制为:早期绑定(Early Binding)和晚期绑定(Late Binding)。绑定是一个字段、方法或者类在符号引用被替换为直接引用的过程,这仅仅发生一次。
虚方法和非虚方法:
如果方法在编译期就确定了具体的调用版本,这个版本在运行时是不可变的。这样的方法称为非虚方法。
静态方法、私有方法、final 方法、实例构造器、父类方法都是非虚方法。其他方法称为虚方法。
虚拟机中提供了以下几条方法调用指令:
普通调用指令:
方法重写的本质:
方法的调用:虚方法表:
在面向对象的编程中,会很频繁的使用到动态分派,如果在每次动态分派的过程中都要重新在类的方法元数据中搜索合适的目标的话就可能影响到执行效率。因此,为了提高性能,JVM 采用在类的方法区建立一个虚方法表 (virtual method table)(非虚方法不会出现在表中)来实现。使用索引表来代替查找。
每个类中都有一个虚方法表,表中存放着各个方法的实际入口。
虚方法表会在类加载的链接阶段被创建并开始初始化,类的变量初始值准备完成之后,JVM 会把该类的方法表也初始化完毕。
如下图:子类或者(接口实现类)对父类(或者接口)重写了(或者实现了)父类的方法,那么虚方法表中存的就是子类的方法,如果没有重写或者实现,就是父类的方法。

被调方法返回值在局部变量表中存放的就是调用方法的pc寄存器,该pc寄存器保存了调用方法应该执行的下一行字节码指令,当前帧出栈后由此回到了调用方法对应的栈,返回值保存在操作数栈中且在栈顶。
方法中定义的局部变量是否线程安全?
具体问题具体分析。如果对象是在内部产生,并在内部消亡,没有返回到外部,那么它就是线程安全的,反之则是线程不安全的。
