JVM栈是由栈帧组成的,一个方法对应一个栈帧,方法执行完后弹栈。数据结构
运行时栈帧结构:栈帧是JVM栈(Stack)中的基本单元,是方法调用和执行的数据结构。对象
每个栈帧(Frame)内部存储有方法的局部变量表(LocalVariables)、操做数栈(OperandStack)、动态链接(CurrentClassConstantPoolReference)、方法返回地址(ReturnValue)和一些额外的附加信息。这些数据在class文件中已肯定,因此每一个栈帧的内存大小不受运行期数据变化的影响。接口
局部变量表(LocalVariables)图片
局部变量表存放方法中用到的8中基本数据类型的值以及对象的引用。对象的引用有两个功能:1,在堆中能找到实际的对象;2,在方法去的中能找到该对象的类型信息。 注意:局部变量必须显式的赋初始值,虚拟机不会对局部变量赋默认值。编译器能够检测这点错误。内存
操做数栈(OperandStack)编译器
存放字节码指令。后入先出。刚开始执行方法时栈内元素为空。操做数栈的大小在class文件中已肯定。虚拟机
动态链接(CurrentClassConstantPoolReference) 编译后的class文件只有符号引用。运行期才能获取实际的引用或句柄。it
方法返回地址(ReturnValue)编译
方法结束的两种方式:1,return;2,异常。 被调用的栈帧的方法返回地址记录了调用的栈帧的执行地址,方便在前者执行完弹栈后PC寄存器能够继续执行后者的代码class
附加信息 虚拟机规范容许具体的虚拟机实现增长一些规范里没有描述的信息到栈帧之中。
方法调用:
方法调用应不等于方法执行,方法调用阶段惟一的任务就是肯定调用哪一个方法,不涉及方法的具体执行。 好比,调用某个接口实现类的某个接口方法,须要在运行期找到接口的具体实现类的这个方法的具体实现。(动态分派)
解析调用是一个静态过程,在编译期间就已彻底肯定,类加载阶段肯定静态方法和私有方法的符号引用转直接引用,在调用时肯定其余方法的符号引用转直接引用。
静态分派: 全部依赖静态类型来定位方法执行版本的分派动做成为静态分派,典型应用好比参数类型为子类和父类的方法重载。 Father f = new Son(). 有两个重载方法 test(Father father) test(Son son),在执行test(f)的时候,编译器就已知是调用的前者,这种定位称为静态分派。
动态分派: Father f = new Son() Father有个方法test(),Son重写了这个test()方法,这调用f.test()的时候定位到后者,这种定位称为动态分派。
单分派和多分派: 同时根据调用者的对象类型和方法参数来定位方法。
方法执行: 在OperatedStatck中执行方法方法调用阶段定位的区MethodCode中的指令集
附上网上找的一张图,经典:
参考资料: