JVM方法调用指令

     终于把Inside JVM这本看完了,好久没这么细致的看一本书了。

     好多人都写了文章讨论jvm如何实现多态的,我只是简单做个笔记。

     类的字节码结构有个常量池,其中就存放了这个类中调用的方法的符号引用,这些符号引用实际上是放在一些特殊类型(CONSTANT_NameAndType_info)的常量池入口中,调用方法有四个指令,invoke_static,invoke_special,invoke_virtual,invoke_interface。jvm指令是调用某个栈上的变量的方法,因此这个变量就有可能指向的是对象或者接口。

刚才提到直接引用,这个在常量池解析时,会将符号引用替换为直接引用。而对于不同情况,直接引用代表的东东是不同的。对于静态类变量,方法指的是本地指针,对于实例方法,指的就是方法表偏移量。

     对于类继承关系而言,从Object开始,所有类的方法的描述的方法在方法表中的索引(也就是在方法表中的位置的偏移量)都是一样的,比如toString方法,无论是在Object,还是在它的子类里,子类的子类里,这个偏移量都是一样的,因此如果栈上的那个变量指向的是一个类实例而不是一个接口,那么就可以替换这个方法调用的常量池入口的符号引用为这个偏移量,真正在运行时调用这个方法时,直接找到那个对象对应的类的方法表,用那个偏移量一下子就可以定位到具体的方法了。

      方法表是类基本信息的一部分,这个可以理解成一个中间表,将偏移量这个逻辑的index与方法真实的本地指针关联了起来(指向方法区,这个区是跟堆一样,所有线程共享的)。

      可以看到,如果是调用静态编译的方法,那么调用时,直接就可以找到本地指针去执行那个方法,如果是指向类的变量调用实例方法,那么就拿放在常量池中的偏移量到类方法表中去查到那个本地指针,再去执行那个方法。

      jvm还有一些指令,加了quick后缀,对于上面两种情况,在常量池解析后,实际上就会直接将原来的指令换为quick版本,将直接引用直接作为指令的操作数,这样就不用跑到常量池中去查一次了。

      那么对于栈中的变量是指向接口的情况怎么办。如果两个毫不相干的类都实现了某个接口,那么对于接口中的一个方法,在两个类的方法表中的偏移量就有可能是不同的,这个时候,在运行时,就只能用方法描述去到类的方法表中现去匹配,当然虚拟机在实现时,会有各种方法提高这个匹配的速度,比如预先分配一个数据结构做索引,或者是把成功匹配到的方法的偏移量做个mapping表,下次再访问时就可以快速找到了。

 

     另外,每调用一个方法时,当前线程就会创建一个frame,然后把它放到当前stack中,每个frame都包括局部变量区,操作数栈和frame变量区。那么一个方法里调用了另一个方法怎么办呢?那就再创建一个frame,去执行那个frame里所有的指令,执行完了再返回刚才那个frame继续执行。因此调用另一个方法是比较耗时的,这才有了方法内联这个优化。在编译类的时候,每个方法占多大内存都是计算好的,运行时中的方法区里保存了这些元数据,创建frame时就根据方法区的内容去创建就好了。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值