深入理解java虚拟机的相关知识(7)--方法调用

方法调用不等于方法执行,方法调用阶段的唯一任务就是确定被调用方法的版本,但不涉及方法内部的具体运行过程。一切方法嗲用在Class文件里面存储的都只是符号引用,而不是方法在实际运行时内存布局中的入口地址,使得java带来更强大的动态扩展能力。

解析

调用目标在程序代码写好,编译器进行编译时就必须确定下来。方法在程序真正运行之前就有一个可确定的调用版本,并且这个方法的调用版本在运行期间是不可改变的。这类方法调用被称为解析
Java虚拟机提供5条方法调用字节码指令,分别如下:
invokestatic:调用静态方法
invokespecial:调用实例构造器方法,私有方法和父类方法。
invokevirtual:调用所有的虚方法。
invokeinterface:调用接口方法,会在运行时在确定一个实现此接口的对象。
invokedynamic:先运行时动态解析出调用点限定符所引用的方法,然后再执行该方法,在此之前的4条调用指令,分派逻辑是固化在java虚拟机内部的,而invokedynamic指令的分派逻辑是由用户所设定的引导方法决定的。

分派

==解析调用一定是个静态的过程,在编译期间就完全确定,在类装载的解析阶段就回吧设计的符号引用全部转变为可确定的直接引用,不会延迟到运行期再去完成。而分派调用则可能是静态的也可能是动态的,可分为静态单分派,静态多分派,动态单分派,动态多分派4种组合情况。

静态分派

所有以来静态类型来定位方法执行版本的分派动作称为静态分派。静态分派的典型应用是方法重载。编译器岁让能确定出方法的重载版本,但在很多情况下这个重载版本并不是唯一的,它的静态类型只能通过语言上的规则去理解和推断。
静态变量的变化仅仅在使用时发生,变量本身的静态类型不会被改变,并且最终的静态类型是在编译器可知的,而实际类型变化的结果在运行期才可确定

动态分派

在运行期间根据实际类型确定方法执行版本的分派过程称为动态分派----重写有密切联系
invokevirtual指令的多态查找过程

  1. 找到操作数栈顶 的第一个元素所指向的对象的实际类型,记做C。
  2. 如果在类型C中找到与常量中的描述符和简单名称都相符的方法,则进行访问权限校验,如果通过则 返回这个方法的直接引用。查找过程结束。不通过则抛异常。
  3. 否则按照继承关系从下往上对C的各个父类进行第2步的搜素和验证过程。
  4. 如果没有找到合适的方法,则抛出异常。

单分派和多分派

方法的接受者和方法的参数统称为方法的宗量,根据分派基于多少种宗量,可以将分派为单分派和多分派两种。
java语言是一门静态多分派,动态单分派的语言。

虚拟机动态分派的实现

通过使用虚方法表索引来代替元数据查找以提高性能。
虚方法表中存放着各个方法的实际入口地址。如果某个方法在子类中没有被重写,那子类的虚方法表里面的地址入口和父类相同方法的地址入口是一致的,都指向父类的实现入口。如果子类重写这个方法,子类方法表中的地址将会替换为指向子类实现版本的入口地址。
方法表一般在类加载的连接阶段进行初始化,准备了类的变量初始值后,虚拟机会把该类的方法表也初始化完毕。
在条件允许的情况下,还会使用内联缓存和基于“类型继承关系分析”技术的守护内联两种非稳定的激进优化手段来获得更高的性能。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值