jvm 之 执行引擎

  1. jvm的字节码执行

    java会编译成字节码 然后在jvm中执行,主要是在栈帧.java的虚拟机执行引擎都是一致的,输入的是字节码,处理过程是字节码解析的过程,输出的是执行结果,

  2. 运行时栈帧的结构

    栈帧存储了,局部变量表,操作数栈,动态连接,返回地址信息,每个线程代表一个栈,一个栈里有很多栈帧,一个栈帧是一个方法,那么一个线程的调用链可能会很长,很多方法在栈中以栈帧形式,那么线程的栈顶的栈帧是有效的,称为当前栈帧

  3. 结构详解

    局部变量表: 存储的是局部变量使用变量槽(Slot)为最小单位为容量,基本是32位大小,对于64位数据是使用两个变量槽 导致64位的数据无法原子性,但栈帧是线程私有数据不会被其他线程访问,不会有线程不安全问题.

    虚拟机通过索引进行使用变量表的 第n个Solt代表是第n个数据,64位的数据是 n,n+1;

    实例方法中,变量表中的第0位索引是用于传递方法所属对象的引用,使用this可以访问这个参数.

    变量表的Solt是可以重用的,当超出作用范围,可以给其他变量使用,并不一定会被gc回收(由于重用).

  4. 操作数栈

    操作栈的最大深度也在编译的时候被写入code的属性max_stacks中,操作数栈的每个元素可以是任意的java数据类型.就像汇编里面的各种指令集的参数进行入栈和出栈的运行,实现执行指令集的目的,由于对于操作并没确定数据类型,因此操作数中的元素类型必须和字节码指令的序列严格匹配(没有检查类型的能力),在编译器就要保证这一点.有些优化会共享一些数据,部分操作数栈和上面的栈帧的局部变量表重叠.

  5. 动态连接

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

  6. 方法返回

    只有两种方法可以退出这个方法,1)执行引擎遇到一个任意方法返回的字节码执行指令,这时候可能会有返回值传递给上层的方法调用者(调用当前方法的方法为调用者)

    2)执行中遇到了异常,这个异常在方法中没有得到处理,无论是java虚拟机内部产生的异常,还是代码中使用athrow字节码指令产生的异常,没有搜到异常处理器就会导致退出.

  7. 方法调用

    7.1解析: 方法在程序真正运行之前就有一个可以确定的调用版本,并且这个版本在运行期不可改变,称为解析.

    的符合"编译期可知,运行期不可变"的要求的方法, 主要包含静态方法和私有方法两种,(前者直接与类直接关联,后者在外部不可被访问 因此)都适合在类加载阶段进行解析.

     invokestatic和invokespecial 都可以在解析阶段中确定唯一版本,符合的有(静态方法,私有方法,实例构造器,父类方法),这些方法,在类加载的时候就会把符号引用用解析为直接引用,可以称为非虚函数.其他的就可以称为虚函数

  8. 分派 

     静态分派 动态分派 用于解释多态中的重载和重写

    静态分派:父类为变量的静态类型,子类为实际类型,静态类型和实际类型在程序中都可以发生一些变化,区别是静态类型的变化仅仅是在使用时发生,变量本身的静态类型不会发生改变,并且最终的静态变量是在编译器可知的.

在重载时是通过参数的静态类型而不是实际类型作为判定依据的等待.

重载时有优先级重载.

动态分派的 例子就是重写:

解释这个现象,就不得不涉及Java虚拟机的invokevirtual指令了,这个指令的解析过程有助于我们更深刻理解重写的本质。该指令的具体解析过程如下:

  1. 找到操作数栈栈顶的第一个元素所指向的对象的实际类型,记为C

  2. 如果在类型C中找到与常量中描述符和简单名称都相符的方法,则进行访问权限的校验,如果通过则返回这个方法的直接引用,查找结束;如果不通过,则返回非法访问异常

  3. 如果在类型C中没有找到,则按照继承关系从下到上依次对C的各个父类进行第2步的搜索和验证过程

  4. 如果始终没有找到合适的方法,则抛出抽象方法错误的异常

具体实现

由于动态分派是非常频繁的操作,实际实现中不可能真正如此实现。Java虚拟机是通过“稳定优化”的手段——在方法区中建立一个虚方法表(Virtual Method Table),通过使用方法表的索引来代替元数据查找以提高性能。虚方法表中存放着各个方法的实际入口地址(由于Java虚拟机自己建立并维护的方法表,所以没有必要使用符号引用,那不是跟自己过不去嘛),如果子类没有覆盖父类的方法,那么子类的虚方法表里面的地址入口与父类是一致的;如果重写父类的方法,那么子类的方法表的地址将会替换为子类实现版本的地址。

<

p style=”box-sizing: border-box; margin-top: 0px; margin-bottom: 1.1em; color: rgb(51, 51, 51); font-family: "microsoft yahei"; white-space: normal; background-color: rgb(255, 255, 255);”>方法表是在类加载的连接阶段(验证、准备、解析)进行初始化,准备了子类的初始化值后,虚拟机会把该类的虚方法表也进行初始化。


评论

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注