JVM

来源:互联网 发布:网络打鱼游戏平台 编辑:程序博客网 时间:2024/06/07 23:02

虚拟机字节码执行引擎:概述,执行引擎:是jvm最核心的部分之一。和物理机相对应。物理机是指直接建立在处理器,硬件,指令集,操作系统层面。虚拟机是自己实现的,自己制定结构体系。

执行过程:
1. 输入字节码文件
2. 处理字节码
3. 输出结果。

执行依靠数据结构-栈帧(stack-frame)存放:
1. 局部变量表
2. 操作数栈
3. 动态链接
4. 方法返回地址
5. 额外的附加信息

在编译期已经确定局部变量表和操作数栈的大小,分配空间已经确定,不会随着程序的运行而改变。
程序运行期,可能会含有多个线程,每个线程有多个栈帧,在活动线程里面,只有栈顶的栈帧是有效的。
关于局部变量表:
存放方法参数和变量,在编译器,就在方法Code属性的max_locals数据项里面分配了空间。局部变量表的最小的单位是:变量槽(Variable Slot)一个solt占用32位空间,一个solt可以存放一个32位数据类型。当遇到64位的数据类型时,会分配两个连续的solt空间。

Solt的复用和GC的关系:
当变量被分配的时候,不会立即引发GC,会在新的数据类型复用原来的内存空间后,会进行垃圾回收。
关于操作数栈:
操作数栈:进行操作数的运算。
关于动态链接:
在类加载时期,符号引用会转换为直接引用称为静态解析。
在运行期,直接转化为直接引用称为动态连接。
关于方法返回地址:
1. 正常完成出口:JVM遇到一个方法返回的字节码出口。
2. 异常退出。

方法调用:
调用的是一个class文件的一个符号引用。
1. invokestatic:调用静态方法;
2. invokespecial:调用实例构造方法,私有方法和父类方法;
3. invokevirtual:调用虚方法和final修饰的方法。
4. invokeinterface:调用接口方法,在运行时再确定一个实现此接口的对象;
5. invokedynamic:在运行时动态解析出调用点限定符所引用的方法之后,调用该方法;
通过invokestatic和invokespecial指令调用的方法,可以在解析阶段确定唯一的调用版本,符合这种条件的有静态方法、私有方法、实例构造器和父类方法4种,它们在类加载时会把符号引用解析为该方法的直接引用。

静态分派:

public class StaticDispatch {    static abstract class Humnan {}    static class Man extends Humnan {}    static class Woman extends Humnan {}    public void hello(Humnan guy) {        System.out.println("hello, Humnan");    }    public void hello(Man guy) {        System.out.println("hello, Man");    }    public void hello(Woman guy) {        System.out.println("hello, Woman");    }    public static void main(String[] args) {        Humnan man = new Man();        Humnan woman = new Woman();        StaticDispatch dispatch = new StaticDispatch();        dispatch.hello(man);        dispatch.hello(woman);    }}hello, Humnanhello, Humnan

通过字节码指令,可以发现两次hello方法都是通过invokevirtual指令进行调用,而且调用的是参数为Human类型的hello方法。

动态分派(重写):

public class DynamicDispatch {    static abstract class Humnan {        abstract void say();    }    static class Man extends Humnan {        @Override        void say() {            System.out.println("hello, Man");        }    }    static class Woman extends Humnan {        @Override        void say() {            System.out.println("hello, Woman");        }    }    public static void main(String[] args) {        Humnan man = new Man();        Humnan woman = new Woman();        man.say();        woman.say();    }}hello, Manhello, Woman

invokevirtual指令的多态查找,invokevirtual指令在运行时分为以下几个步骤:
1. 找到操作数栈的栈顶元素所指向的对象的实际类型,记为C;
2. 如果C中存在描述符和简单名称都相符的方法,则进行访问权限验证,如果验证通过,则直接返回这个方法的直接引用,否则返回java.lang.IllegalAccessError异常;
3. 如果C中不存在对应的方法,则按照继承关系对C的各个父类进行第2步的操作;
4. 如果各个父类也没对应的方法,则返回异常;
所以上述两次invokevirtual指令将相同的符号引用解析成了不同对象的直接引用,这个过程就是Java语言中重写的本质。

JVM动态分派实现
由于动态分派是非常频繁的动作,因此在虚拟机的实际实现中,会基于性能的考虑,并不会如此频繁的搜索对应方法,一般会在方法区中建立一个虚方法表,使用虚方法表代替方法查询以提高性能。
虚方法表在类加载的连接阶段进行初始化,存放着各个方法的实际入口地址,如果某个方法在子类中没有被重写,那么子类的虚方法表中该方法的入口地址和父类保持一致。

基于Stack的字节码解释执行引擎:
解释器执行。
程序在JVM里面的实现过程(基于stack)。
例如:
如果有多个变量声明,会先把变量push进入操作栈,接着istore指令会将操作栈里面的变量放入局部变量表。接着进行运算,最后返回方法函数值。

原创粉丝点击