JVM规范学习:invokevirtual

本文参考:http://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.invokevirtual

invokevirtual

操作

调用实例方法,基于类进行分派

格式

invokevirtual
indexbyte1
indexbyte2

编码

invokevirtual = 182 (0xb6)

操作数栈

objectref, [arg1, [arg2 ...]] →

描述

indexbyte1indexbyte2均为无符号字节,用来构造指向当前Class的运行时常量池的索引index,该索引通过如下方式构造:(indexbyte1<< 8) | indexbyte2.运行时常量池在索引index的位置必须为一个指向方法的符号引用,该符号引用包含了该方法的名称,描述符以及定义该方法的类的符号引用。并且,该方法会被解析,解析的结果不能为实例初始化方法,类或者接口的初始化方法。最终,如果解析出来的方法是Protected类型,并且:该方法是当前类的父类的成员函数以及该方法没有在当前类所属的运行时Package中声明,那么:objectref的类型必须是当前类或者当前类的子类。

如果解析出来的方法不是签名多态的,那么invokevirtual指令按照如下方式处理

假设objectref的类型为C,实际调用的方法将按照下面流程来查找:

  • 如果C包含了一个方法m的声明,该声明重写了解析出来的方法,则m为实际调用的方法,查找终止。
  • 否则:如果C有父类,则自下而上递归的在C的直接父类中执行第一步的查找。
  • 如果还没有查找到,则抛出AbstractMethodError错误。

如果解析出来的方法有参数的话,在操作数栈上,所有的参数的值必须按照方法描述中规定的数量,类型和顺序自上而下跟在objectref后面。

 

如果是同步方法,则和在当前线程中执行monitorenter指令一样在和当前objectref关联的监视器monitor上执行entered和reentered操作。

 

如果解析出来的方法不是native的,所有的参数和objectref从操作数栈中弹出。在JVM栈上为当前准备调用的方法创建一个新的栈帧。Objectref和所有参数被按照如下顺序作为新栈帧的局部变量,objectref占slot0,arg1占slot1(否则,如果arg1为long或double类型,则占slot1和slot2),以此类推。对于float类型参数,在存入局部变量表之前会先进行值集转换。新的栈帧成为当前栈帧,JVM的PC设置为即将调用方法的第一条指令的操作码,程序执行从第一条指令继续进行。

如果解析出来的方法是native的,并且实现该方法的平台相关的代码尚未绑定到当前JVM。所有参数和objectref从操作数栈中弹出,作为参数传递给实现该方法的代码。任何float类型的参数在传递之前都会进行值集的转换。该native方法按照平台相关的方式执行,方法返回时,将会发生如下这些结果:

  •  如果该native方法是同步的(synchronized),则,和objectref关联的监视器(monitor)会被更新,并有可能退出,就像在当前线程中执行monitorexit执行一样。
  •  如果该native方法有返回值,则,该返回值依赖于具体实现的方式转换成该native方法的返回类型并压入操作数栈。

如果解析出来的方法是签名多态的,那么invokevirtual指令按照如下方式处理

首先,获取一个java.lang.invoke.MethodType类型的实例引用,就像解析一个和当前invokevirtual指令引用的方法具有相同的参数和返回值类型一样的method type的符号引用一样。

  • 如果解析出来的方法是invokeExact,则,MethodType实例的类型必须在语义上和接收方法句柄objectref的类型描述符一致,objectref为将要调用的方法句柄。
  • 如果解析出来的方法是invoke,并且,MethodType实例的类型必须在语义上和接收方法句柄objectref的类型描述符一致,objectref为将要调用的方法句柄。
  • 如果解析出来的方法是invoke,并且,MethodType实例的类型在语义上和接收方法句柄objectref的类型描述符不一致,则JVM会试图调整接收方法句柄的类型,就像调用java.lang.invoke.MethodHandle.asType一样,去获取一个准确的可调用的方法句柄m,m将作为即将调用的方法句柄。

 

在操作数栈中,调用参数必须跟在objectref后面,数量,类型以及顺序都必须和调用的方法句柄的类型描述一致(这个类型描述符将相当于适合于将要调用的方法句柄的相应类型的方法描述符)。

然后,如果该方法句柄有字节码行为,则JVM就像执行和其关联的类型一样的字节码一样来执行该句柄。如果句柄的类型是5 (REF_invokeVirtual), 6 (REF_invokeStatic), 7 (REF_invokeSpecial), 8 (REF_newInvokeSpecial), or 9 (REF_invokeInterface),那么,在执行字节码期间,一个新的栈帧将会被创建并作为当前栈帧,当该调用方法执行完(正常或异常)后当,当前栈帧将恢复到包含invokevirtual指令的栈帧。

字节码自身执行的栈帧是不可见的。

否则,如果该调用方法句柄没有字节码行为,JVM对其的调用取决于具体的JVM实现方式。

链接异常

在方法符号引用解析期间,任何与方法解析相关的异常都可以抛出。

并且,如果解析出来的方法是类方法(static),invokevirtual指令将抛出IncompatibleClassChangeError错误。

并且,如果解析出来的方法是签名多态的,那么,在解析由该方法的符号引用包含的描述符推导出的方法类型期间,可以与方法类型解析相关的异常都可以被抛出。

运行时异常

如果objectref为NULL,invokevirtual指令将抛出NullPointerException异常。

如果解析出来的方法不是签名多态的:

  • 如果没有方法和符号引用包含的名字和描述符匹配,则invokevirtual指令将抛出AbstractMethodError错误。
  • 如果解析出来的方法是abstract的,则invokevirtual指令将抛出AbstractMethodError错误。
  • 如果解析出来的方法是native的,但是,具体的实现代码无法绑定,invokevirtual指令会抛出UnsatisfiedLinkError错误。

如果解析出来的方法是签名多态的:

  • 如果解析的方法是invokeExact,并且获取到的java.lang.invoke.MethodType实例在语义上和方法接收句柄的类型描述不一致,invokevirtual指令将抛出java.lang.invoke.WrongMethodTypeException异常。
  • 如果解析出来的方法是invoke,并且获取到的java.lang.invoke.MethodType实例不是在方法接收句柄上执行的java.lang.invoke.MethodHandle.asType方法的有效参数,则invokevirtual指令将抛出java.lang.invoke.WrongMethodTypeException异常。

注意

nargs个数的参数和objectref并不一定刚好对应nargs+1个局部变量,对于long和double类型的参数,将对应两个slot。这样在传递参数到被调用方法时,所需的slot个数将超过nargs+1个。

 

 

 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值