zoukankan      html  css  js  c++  java
  • 第34篇-解析invokeinterface字节码指令

    与invokevirtual指令类似,当没有对目标方法进行解析时,需要调用LinkResolver::resolve_invoke()函数进行解析,这个函数会调用其它一些函数完成方法的解析,如下图所示。

    上图中粉色的部分与解析invokevirtual字节码指令有所区别,resolve_pool()函数及其调用的相关函数在介绍invokevirtual字节码指令时详细介绍过,这里不再介绍。

    调用LinkResolver::resolve_invokeinterface()函数对字节码指令进行解析。函数的实现如下:

    void LinkResolver::resolve_invokeinterface(
     CallInfo&            result,
     Handle               recv,
     constantPoolHandle   pool,
     int                 index, // 指的是常量池缓存项的索引
     TRAPS
    ) {
      KlassHandle  resolved_klass;
      Symbol* method_name = NULL;
      Symbol* method_signature = NULL;
      KlassHandle  current_klass;
      // 解析常量池时,传入的参数pool(根据当前栈中要执行的方法找到对应的常量池)和
      // index(常量池缓存项的缓存,还需要映射为原常量池索引)是有值的,根据这两个值能够
      // 解析出resolved_klass和要查找的方法名称method_name和方法签名method_signature
      resolve_pool(resolved_klass, method_name,  method_signature, current_klass, pool, index, CHECK);
    
      KlassHandle recvrKlass (THREAD, recv.is_null() ? (Klass*)NULL : recv->klass());
      resolve_interface_call(result, recv, recvrKlass, resolved_klass, method_name, method_signature, current_klass, true, true, CHECK);
    }

    我们接着看resolve_interface_call()函数的实现,如下:

    void LinkResolver::resolve_interface_call(
     CallInfo&        result,
     Handle           recv,
     KlassHandle      recv_klass,
     KlassHandle      resolved_klass,
     Symbol*          method_name,
     Symbol*          method_signature,
     KlassHandle      current_klass,
     bool             check_access,
     bool            check_null_and_abstract,
     TRAPS
    ) {
      methodHandle resolved_method;
      linktime_resolve_interface_method(resolved_method, resolved_klass, method_name, method_signature, current_klass, check_access, CHECK);
      runtime_resolve_interface_method(result, resolved_method, resolved_klass, recv, recv_klass, check_null_and_abstract, CHECK);
    }
    

    调用2个函数对方法进行解析。首先看linktime_resolve_interface_method()函数的实现。

    调用linktime_resolve_interface_method()函数会调用LinkResolver::resolve_interface_method()函数,此函数的实现如下:

    void LinkResolver::resolve_interface_method(
     methodHandle& resolved_method,
     KlassHandle   resolved_klass,
     Symbol*       method_name,
     Symbol*       method_signature,
     KlassHandle   current_klass,
     bool          check_access,
     bool          nostatics,
     TRAPS
    ) {
      // 从接口和父类java.lang.Object中查找方法,包括静态方法
      lookup_method_in_klasses(resolved_method, resolved_klass, method_name, method_signature, false, true, CHECK);
    
      if (resolved_method.is_null()) {
        // 从实现的所有接口中查找方法
        lookup_method_in_interfaces(resolved_method, resolved_klass, method_name, method_signature, CHECK);
        if (resolved_method.is_null()) {
          // no method found
          // ...
        }
      }
    
      // ...
    }

    首先调用LinkResolver::lookup_method_in_klasses()函数进行方法查找,在之前介绍过invokevirtual字节码指令时介绍过这个函数,不过只介绍了与invokevirtual指令相关的处理逻辑,这里需要继续查看invokeinterface的相关处理逻辑,实现如下: 

    void LinkResolver::lookup_method_in_klasses(
     methodHandle&  result,
     KlassHandle    klass,
     Symbol*        name,
     Symbol*        signature,
     bool          checkpolymorphism,
     // 对于invokevirtual来说,值为false,对于invokeinterface来说,值为true
     bool          in_imethod_resolve,
     TRAPS
    ) {
      Method* result_oop = klass->uncached_lookup_method(name, signature);
    
      // 在接口中定义方法的解析过程中,忽略Object类中的静态和非public方法,如
      // clone、finalize、registerNatives
      if (
          in_imethod_resolve &&
          result_oop != NULL &&
          klass->is_interface() &&
          (result_oop->is_static() || !result_oop->is_public()) &&
          result_oop->method_holder() == SystemDictionary::Object_klass() // 方法定义在Object类中
      ) {
        result_oop = NULL;
      }
    
      if (result_oop == NULL) {
        Array<Method*>* default_methods = InstanceKlass::cast(klass())->default_methods();
        if (default_methods != NULL) {
          result_oop = InstanceKlass::find_method(default_methods, name, signature);
        }
      }
      // ...
      result = methodHandle(THREAD, result_oop);
    }

    调用uncached_lookup_method()函数从当前类和父类中查找,如果没有找到或找到的是Object类中的不合法方法,则会调用find_method()函数从默认方法中查找。在Java8的新特性中有一个新特性为接口默认方法,该新特性允许我们在接口中添加一个非抽象的方法实现,而这样做的方法只需要使用关键字default修饰该默认实现方法即可。

    uncached_lookup_method()函数的实现如下:

    Method* InstanceKlass::uncached_lookup_method(Symbol* name, Symbol* signature) const {
      Klass* klass = const_cast<InstanceKlass*>(this);
      bool dont_ignore_overpasses = true;  
      while (klass != NULL) {
        Method* method = InstanceKlass::cast(klass)->find_method(name, signature);
        if ((method != NULL) && (dont_ignore_overpasses || !method->is_overpass())) {
          return method;
        }
        klass = InstanceKlass::cast(klass)->super();
        dont_ignore_overpasses = false;  // 不要搜索父类中的overpass方法
      }
      return NULL;
    }

    从当前类和父类中查找方法。当从类和父类中查找方法时,调用find_method()函数,最终调用另外一个重载函数find_method()从InstanceKlass::_methods属性中保存的方法中进行查找;当从默认方法中查找方法时,调用find_method()函数从InstanceKlass::_default_methods属性中保存的方法中查找。重载的find_method()函数的实现如下:

    Method* InstanceKlass::find_method(Array<Method*>* methods, Symbol* name, Symbol* signature) {
      int hit = find_method_index(methods, name, signature);
      return hit >= 0 ? methods->at(hit): NULL;
    }

    其实调用find_method_index()函数就是根据二分查找来找名称为name,签名为signature的方法,因为InstanceKlass::_methods和InstanceKlass::_default_methods属性中的方法已经进行了排序,关于这些函数中存储的方法及如何进行排序在《深入剖析Java虚拟机:源码剖析与实例详解(基础卷)》一书中详细介绍过,这里不再介绍。

    调用的LinkResolver::runtime_resolve_interface_method()函数的实现如下:

    void LinkResolver::runtime_resolve_interface_method(
     CallInfo&     result,
     methodHandle  resolved_method,
     KlassHandle   resolved_klass,
     Handle        recv,
     KlassHandle   recv_klass,
     bool         check_null_and_abstract, // 对于invokeinterface来说,值为false
     TRAPS
    ) {
      // ...
    
      methodHandle sel_method;
    
      lookup_instance_method_in_klasses(
                sel_method, 
                recv_klass,
                resolved_method->name(),
                resolved_method->signature(), 
                CHECK);
    
      if (sel_method.is_null() && !check_null_and_abstract) {
        sel_method = resolved_method;
      }
    
      // ...
      // 如果查找接口的实现时找到的是Object类中的方法,那么要通过vtable进行分派,所以我们需要
      // 更新的是vtable相关的信息
      if (!resolved_method->has_itable_index()) {
        int vtable_index = resolved_method->vtable_index();
        assert(vtable_index == sel_method->vtable_index(), "sanity check");
        result.set_virtual(resolved_klass, recv_klass, resolved_method, sel_method, vtable_index, CHECK);
      } else {
        int itable_index = resolved_method()->itable_index();
        result.set_interface(resolved_klass, recv_klass, resolved_method, sel_method, itable_index, CHECK);
      }
    }
    

    当没有itable索引时,通过vtable进行动态分派;否则通过itable进行动态分派。 

    调用的lookup_instance_method_in_klasses()函数的实现如下:

    void LinkResolver::lookup_instance_method_in_klasses(
     methodHandle&  result,
     KlassHandle    klass,
     Symbol*        name,
     Symbol*        signature,
     TRAPS
    ) {
      Method* result_oop = klass->uncached_lookup_method(name, signature);
      result = methodHandle(THREAD, result_oop);
      // 循环查找方法的实现,不会查找静态方法
      while (!result.is_null() && result->is_static() && result->method_holder()->super() != NULL) {
        KlassHandle super_klass = KlassHandle(THREAD, result->method_holder()->super());
        result = methodHandle(THREAD, super_klass->uncached_lookup_method(name, signature));
      }
    
      // 当从拥有Itable的类或父类中找到接口中方法的实现时,result不为NULL,
      // 否则为NULL,这时候就要查找默认的方法实现了,这也算是一种实现
      if (result.is_null()) {
        Array<Method*>* default_methods = InstanceKlass::cast(klass())->default_methods();
        if (default_methods != NULL) {
          result = methodHandle(InstanceKlass::find_method(default_methods, name, signature));
        }
      }
    }

    如上在查找默认方法实现时会调用find_method()函数,此函数在之前介绍invokevirtual字节码指令的解析过程时详细介绍过,这里不再介绍。

    在LinkResolver::runtime_resolve_interface_method()函数的最后有可能调用CallInfo::set_interface()或CallInfo::set_virtual()函数,调用这两个函数就是将查找到的信息保存到CallInfo实例中。最终会在InterpreterRuntime::resolve_invoke()函数中根据CallInfo实例中保存的信息更新ConstantPoolCacheEntry相关的信息,如下:

    switch (info.call_kind()) {
      // ...
      case CallInfo::itable_call:
        cache_entry(thread)->set_itable_call(
    		  bytecode,
    		  info.resolved_method(),
    		  info.itable_index());
        break;
      default:  ShouldNotReachHere();
    }

    当CallInfo中保存的是itable的分派信息时,调用set_itable_call()函数,这个函数的实现如下:

    void ConstantPoolCacheEntry::set_itable_call(
     Bytecodes::Code   invoke_code,
     methodHandle      method,
     int               index
    ) {
      assert(invoke_code == Bytecodes::_invokeinterface, "");
      InstanceKlass* interf = method->method_holder();
      // interf一定是接口,而method一定是非final方法
      set_f1(interf); // 对于itable,_f1保存的是表示接口的InstanceKlass
      set_f2(index);  // 对于itable,_f2保存的是itable索引
      set_method_flags(as_TosState(method->result_type()),
                       0,  // no option bits
                       method()->size_of_parameters());
      set_bytecode_1(Bytecodes::_invokeinterface);
    }

    使用CallInfo实例中的信息更新ConstantPoolCacheEntry中的信息即可。  

    推荐阅读:

    第1篇-关于JVM运行时,开篇说的简单些

    第2篇-JVM虚拟机这样来调用Java主类的main()方法

    第3篇-CallStub新栈帧的创建

    第4篇-JVM终于开始调用Java主类的main()方法啦

    第5篇-调用Java方法后弹出栈帧及处理返回结果

    第6篇-Java方法新栈帧的创建

    第7篇-为Java方法创建栈帧

    第8篇-dispatch_next()函数分派字节码

    第9篇-字节码指令的定义

    第10篇-初始化模板表

    第11篇-认识Stub与StubQueue

    第12篇-认识CodeletMark

    第13篇-通过InterpreterCodelet存储机器指令片段

    第14篇-生成重要的例程

    第15章-解释器及解释器生成器

    第16章-虚拟机中的汇编器

    第17章-x86-64寄存器

    第18章-x86指令集之常用指令

    第19篇-加载与存储指令(1)

    第20篇-加载与存储指令之ldc与_fast_aldc指令(2)

    第21篇-加载与存储指令之iload、_fast_iload等(3)

    第22篇-虚拟机字节码之运算指令

    第23篇-虚拟机字节码指令之类型转换

    第24篇-虚拟机对象操作指令之getstatic

    第25篇-虚拟机对象操作指令之getfield

    第26篇-虚拟机对象操作指令之putstatic

    第27篇-虚拟机字节码指令之操作数栈管理指令

    第28篇-虚拟机字节码指令之控制转移指令

    第29篇-调用Java主类的main()方法

    第30篇-main()方法的执行

    第31篇-方法调用指令之invokevirtual

    第32篇-解析interfacevirtual字节码指令

    第33篇-方法调用指令之invokeinterface

      

      

     

  • 相关阅读:
    mysql实现主从备份
    Spring boot 继承 阿里 autoconfig 配置环境参数
    Spring Boot 注解的使用
    SpringBoot yml 配置
    浅谈提高工作效率
    Oracle 数据库特殊查询总结
    WPF MVVM 学习总结(一)
    VS2010部署Asp.net程序到本地IIS 7
    Asp.net MVC3表格共用分页功能
    WCF学习总结
  • 原文地址:https://www.cnblogs.com/mazhimazhi/p/15492114.html
Copyright © 2011-2022 走看看