jvm之虚拟机字节码执行引擎(续)

之前学习了静态分派和动态分派,静态分派典型的应用是方法重载,动态分派典型的应用的方法重写。

静态分派在编译阶段就确定,也就是在编译阶段就确定了invokeviryual指令,也就是确定了方法的签名。

在动态分派的过程中,也就是当执行到这条invokevirtual指令时候,方法的实际接收者确定步骤有以下几步:

1.找到操作数栈顶的第一个元素所指向的对象的实例类型

2.如果在类型C中找到方法签名和相同的方法,则进行权限校验,如果通过返回这个方法的引用,如果不通过,抛出异常java.lang.IllegalAccessError。

3.否则按照继承关系从下到上依次找父类

4.如果始终没有,则抛出java.lang.AbstractMethodError

这里就可以看出来,对象的静态类型决定了方法的签名,也就决定的调用哪个方法,即方法重载的应用。对象的实例类型决定了调用谁的方法,即方法重写的应用

例如obj.method(new Obj()),确定这一条方法的调用,在编译阶段要基于两个宗量,首先是对象的静态类型,然后是方法参数的静态类型。

在运行阶段,虚拟机执行到这条指令时候,不需要关系方法的参数的类型,只需要关系接收方法的对象。

所以静态分配时多分派。动态分配是单分派。


虚拟机动态分派实现:

因为动态分配需要在类的方法的元数据中搜索合适的方法,虚拟机基于性能的考虑,会做优化。最常用的手段是在类的方法区中建立一个虚方法表,使用虚方法表来优化搜索方法的速度。

虚方法表中存放各个方法的实际入口,如果子类没有重写父类的方法,则子类中的方法入口与父类一致。一般为了程序实现方便,在子类与父类中相同签名的方法都会在虚方法表中有相同的索引位置,这样在进行类型转换时,只需要变更虚方法表即可。


动态类型语言:

动态类型语言的特点是他类型检查的主体是在运行期而不是编译期。

例如  obj.println(“。。。”);这一行代码,在java中,如果obj的静态类型是java.io.PrintStream,那么obj的实例类型一定是PrintStream的子类,否则编译无法通过,这与上面我们学习的静态分配有关,因为在编译阶段,生成的invokevirtual指令是根据obj的静态类型,所以会把如下指令写到class文件中:java/io/PrintStream.println:(Ljava/lang/String;)V。

这个符号引用中包含了静态类型。

而动态类型语言,变量是没有类型的,变量的值才有类型,所以编译阶段只会确定方法名,参数,返回值,不会像java编译阶段就确定了类型。




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值