虚拟机字节码执行

运行时栈帧结构
栈帧是用来支持虚拟机进行方法调用和方法执行的数据结构,它是虚拟机运行时数据区中的虚拟机栈的栈元素。
栈帧存储了方法的局部变量表、操作数栈、动态连接、和方法返回地址等信息。
方法的调用到执行完成对应一个栈帧的入栈到出栈过程。
局部变量表
一组变量值存储空间,用于存放方法参数和方法内部定义的局部变量。
在编译期确定大小,max_locals
单位slot(32位)
八种数据的类型:byte short int char boolean float reference returnAddress
reference(引用类型):对象实例的引用:找到实例对象在堆中初始地址索引;找到对象方法区的类型信息
64位数据类型long double,可用两个连续的slot,原子操作?,局部变量建立在线程的堆栈上不存在线程安全问题,不允许访问其中的一个,
执行实例方法时,那局部变量表中第0位索引:指向实例对象的应用(可用this调用此引用),从第1位开始分配局部变量Slot,参数表-->再根据内部定义的局部变量顺序+作用域分配其余的Slot
为了节省空间,Slot是可以重用的:场景方法中作用域内的局部变量超出作用域之后,
副作用是影响GC行为,延迟GC,超出作用域后,作用域内局部变量slot未被占用,GC可能不会回收
不使用的对象应手动赋值为null,但是;以恰当的变量作用域来控制变量回收时间才是最优雅的解决方案,公有设计,私有实现,赋值为null JIT优化后会被消除,而且JIT后本地代码GCRoot和解释执行期有很大的区别。
操作数栈
先入后出
编译期确定大小,写入到Code属性的max_stacks数据项
每个栈元素可以是任意类型的java数据类型,32位数据类型栈容量为1,,64为2
方法开始执行时,操作数栈时空的,执行过程中,会有各种字节码指令往操作数栈中写入和提取内容,入栈、出栈操作。
编译期:操作数栈中的元素数据类型必须与字节码指令的序列严格匹配。
概念模型中连个栈帧作为虚拟机栈的元素是完全独立的,但是大多数虚拟机实现都会做一些优化,两栈部分局部变量表和部分操作数栈重叠。
动态链接
每个栈帧都包含一个指向运行时常量池中该栈帧所属方法的引用。静态解析与动态链接。
方法返回地址
正常完成出口:执行引擎遇到任意一个方法返回的字节码指令,导致方法退出。
异常完成出口:在本方法的异常表中没有搜索到匹配的异常处理器,导致方法退出。
附加信息
 
方法调用:
方法调用并不等同与方法执行,方法调用阶段唯一任务:确定被调用方法的版本(调用哪一个方法)
解析
类加载的解析阶段,会将一部分符号引用转化为直接引用--编译期可知,运行期不可变
虚拟机提供5条方法调用字节码指令
invokestatic:调用静态方法
invokespecial:调用实例构造器<init>方法,私有方法和父类方法
invokevirtual:调用所有的虚方法
invokeinterface:调用接口方法,会在运行时再确定一个实现此接口的对象。
invokedynamic:现在运行时动态解析出调用点限定符所引用的方法,然后在执行该方法。
其中被invokestatic、invokespecial指令调用的方法,都可以在解析阶段中确定唯一的调用版本:
 
分派
静态方法,私有方法、实例构造器、父类方法--非虚方法
解析调用一定是一个静态的过程;分派调用则可能是静态的也可能是动态的
静态单分派、静态多分派、动态单分派、动态多分派
静态分派
静态类型(外观类型)和实际类型
所有以来静态类型来定位方法执行版本的分派动作成为静态分派---典型应用方法重载
静态分派发生在编译阶段,不是有虚拟机来执行的
字面量类型‘a’ 1等
本类型--自动转换--自动装箱--接口--父类--可变参数
解析和分派并不是二选一的互斥关系,静态方法的重载 
动态分派是根据实际类型来调用方法的
 
动态类型支持,典型的动态语言JavaScript,javaScript,类型检查在运行期。

猜你喜欢

转载自www.cnblogs.com/wqff-biubiu/p/10402874.html