您的位置:首页 > 编程语言 > Java开发

深入理解java虚拟机-读书笔记5-虚拟机字节码执行引擎

2016-12-01 22:34 357 查看
虚拟机字节码执行引擎的概念模型,在执行java代码的时候可能会有解释执行和编译执行两种选择。所有的执行引擎都是:输入的是字节码文件,处理过程是字节码解析的等效过程,输出的是执行结果。

运行时栈帧结构

栈帧是支持虚拟机进行方法调用和执行的数据结构,是虚拟机运行时数据区中的虚拟机栈的栈元素。存储了方法的局部变量表,操作数栈,动态连接和方法返回地址等信息。

在编译时,一个栈帧需要分配多少内存就完全确定了。只有当前栈帧是有效的。



局部变量表

存放方法参数和局部变量。以变量槽(Slot)存放数据,变量槽有索引,从0开始。0是当前对象的引用,用this关键字可以获取。其余是其他参数和局部变量,虚拟机通过索引取值。局部变量不像类变量,不会赋初始值,没赋值过就不能使用。

操作数栈

方法刚进入时操作数栈是空的,方法执行过程中会有指令不停进出栈。

动态连接

每个栈帧包含一个指向运行时常量池中该栈帧所属方法的引用,为了支持方法调用过程中的动态连接。

方法返回地址

在方法退出之后,需要返回到方法被调用的位置,保存这个信息。

附加信息

如调试相关信息。

方法调用

调用并不等于方法执行,调用阶段唯一的任务是:确定被调用方法的版本,不涉及具体运行过程。

解析

在类加载的解析阶段,会将一部分符号引用转化为直接引用,这种解析成立的前提是:方法在运行前就有一个可确定的调用版本,并且此版本在运行期不可改变。这类方法的调用成为解析。

符合“编译器可知,运行期不可变”要求的,主要包括:静态方法和私有方法。

分派

静态分派

所有依赖静态类型来定位方法执行版本的分派动作成为静态分派。典型应用是方法重载。静态分派发生在编译阶段,因此不是由虚拟机来执行的。



这个例子的输出结果是:

hello, guy!

hello, guy!

我们把Human成为变量的静态类型,Man成为变量的实际类型。重载时时通过参数的静态类型而不是实际类型作为判定依据的。

动态分派

在运行期根据实际类型确定方法执行版本的分派过程成为动态分派。典型
965d
应用时方法重写



这个例子的输出结果是:

hello man!

hello women!

hello women!

invokevirtual指令的过程大概是,找到找到操作数栈顶第一个元素的实际类型C。在C中查找与常量中完全相符的方法,如果找不到就层层往上,查找父类。这个过程第一步就是找实际类型,然后直接找方法执行。

单分派与多分派

方法的接收者与方法的参数统称为宗量,根据分派基于多少种宗量,将分派分为单分派和多分派。

java语言是一门静态多分派、动态单分派的语言。

虚拟机动态分派的实现

动态类型语言支持

什么是动态类型语言?关键特征就是:类型检查的主题过程是在运行期而不是编译器,常用的包括:Groovy、JavaScript、Ruby等。

java.lang.invoke包目的是,提供一种新的动态确定目标方法的机制,称为MethodHandle。可以实现对方法的调用。

MethodHandle和反射的区别:

反射是模拟java代码层次的方法调用,而MethodHandle是在模拟字节码层次的方法调用

反射中java.lang.reflect.Method对象远比MethodHandle机制中java.lang.invoke.MethodHandle对象包含的信息多。反射是重量级,MethodHandle是轻量级

反射是为了Java服务的,而MehtodHandler是为了虚拟机服务

基于栈的字节码解释执行引擎

执行引擎在执行java代码时都有解释执行(通过解释器执行)和编译执行(通过即时编译器产生本地代码执行)两种选择。

比如1+1的计算

基于栈的指令集:两个1入栈,然后出栈、相加,然后把结果放回栈顶。

基于寄存器的指令集:把EAX寄存器的值设为1,把这个值加1,结果保存在EAX寄存器中。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: