【JVM源码解析】虚拟机解释执行Java方法(下)

本文详细解析了JVM中invokeinterface、invokespecial和invokestatic字节码指令的执行过程,包括方法解析、动态分派、返回指令return的处理,以及方法调用间的交互。通过源码分析,揭示了JVM如何执行Java方法并处理不同类型的调用指令。
摘要由CSDN通过智能技术生成

本文由HeapDump性能社区首席讲师鸠摩(马智)授权整理发布

第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 = methodHand
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

HeapDump性能社区

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值