专注虚拟机与编译器研究

第6.4篇-计算vtable的大小

在ClassFileParser::parseClassFile()函数中会计算vtable和itable所需要的大小,因为vtable和itable是内嵌在Klass中的,parseClassFile()函数解析完Class文件后会创建instanceKlass来保存相关的信息,在创建instanceKlass时需要知道创建对象的大小,所以必须要把vtable和itable也计算出来。下面就来介绍一下vtable和itable大小的计算过程,这一篇只介绍vtable大小的计算过程,下一篇将介绍itable大小的计算过程。

在ClassFileParser::parseClassFile()函数中首先计算vtable的大小,如下:

// Size of Java vtable (in words)
int   vtable_size = 0;
int   itable_size = 0;
int   num_miranda_methods = 0;
GrowableArray<Method*>  all_mirandas(20);
InstanceKlass*          tmp = super_klass();
// 计算虚函数表的大小和mirandas方法的数量,这个虚函数表的值是什么时候进行填充的呢???
klassVtable::compute_vtable_size_and_num_mirandas(
				&vtable_size,
				&num_miranda_methods,
				&all_mirandas,
				tmp,
				methods,
				access_flags,
				class_loader,
				class_name,
				local_interfaces,
				CHECK_(nullHandle)
			);

调用方法时传递的methods就是调用parse_methods()方法后的返回值,数组中存储了类或接口中定义或声明的所有方法。

调用的compute_vtable_size_and_num_mirandas()方法的实现如下:

void klassVtable::compute_vtable_size_and_num_mirandas(
			int* vtable_length_ret,
			int* num_new_mirandas,
			GrowableArray<Method*>* all_mirandas,
			Klass* super,
			Array<Method*>* methods,
			AccessFlags class_flags,
			Handle classloader,
			Symbol* classname,
			Array<Klass*>* local_interfaces,
			TRAPS) {

  // set up default result values
  int vtable_length = 0;

  // start off with super's vtable length
  InstanceKlass* superklass = (InstanceKlass*)super;
  // 获取父类 vtable 的大小,并将当前类的 vtable 的大小设置为父类 vtable 的大小
  vtable_length = super == NULL ? 0 : superklass->vtable_length();

  // go thru each method in the methods table to see if it needs a new entry
  int len = methods->length();
  for (int i = 0; i < len; i++) {
    methodHandle mh(THREAD, methods->at(i));

    // 循环遍历当前 Java 类的每一个方法 ,调用 needs_new_vtable_entry()函数进行判断,
    // 如果判断的结果是 true ,则将 vtable 的大小增 1
    if (needs_new_vtable_entry(mh, super, classloader, classname, class_flags, THREAD)) {
       vtable_length += vtableEntry::size(); // we need a new entry
    }
  }

  GrowableArray<Method*> new_mirandas(20);
  // compute the number of mirandas methods that must be added to the end
  get_mirandas(&new_mirandas, all_mirandas, super, methods, NULL, local_interfaces);
  *num_new_mirandas = new_mirandas.length();

  // Interfaces do not need interface methods in their vtables
  // This includes miranda methods and during later processing, default methods
  if (!class_flags.is_interface()) { // 只有类才需要处理miranda方法,接口不需要处理
    // 类的vtable大小要加上miranda方法的大小
    vtable_length += *num_new_mirandas * vtableEntry::size();
  }

  // 处理数组类时,其vtable_length应该等于Object的vtable_length,通常为5,因为Object中有5个方法需要动态绑定
  if (Universe::is_bootstrapping() && vtable_length == 0) {
     // array classes don't have their superclass set correctly during bootstrapping
     vtable_length = Universe::base_vtable_size();
  }

  *vtable_length_ret = vtable_length;
}

vtable的大小通常都是由3部分计算得出:

父类vtable的大小+当前方法需要的vtable大小+mirandas需要的大小

下面会重点介绍needs_new_vtable_entry()方法和get_mirandas()方法,前一个方法对计算当前方法需要的vtable大小很重要,后一个方法是计算mirandas方法需要的大小。

1、needs_new_vtable_entry()方法

循环处理当前类中定义的方法,调用needs_new_vtable_entry()方法判断此方法是否需要新的vtable entry,因为有些方法可能不需要新的vtable entry,如重写父类方法时,当前类中的方法只需要更新拷贝自父类vtable中对应的vtable entry即可。调用的needs_new_entry()方法的实现如下:

源代码位置:hotspot/src/share/vm/oops/klassVtable.cpp
// Find out if a method "m" with superclass "super", loader "classloader" and
// name "classname" needs a new vtable entry.  Let P be a class package defined
// by "classloader" and "classname".
// NOTE: The logic used here is very similar to the one used for computing
// the vtables indices for a method. We cannot directly use that function because,
// we allocate the InstanceKlass at load time, and that requires that the
// superclass has been loaded.
// However, the vtable entries are filled in at link time(在连接时才会被填充), and therefore
// the superclass' vtable may not yet have been filled in.
bool klassVtable::needs_new_vtable_entry(methodHandle target_method,
                                         Klass*       super,
                                         Handle       classloader,
                                         Symbol*      classname,
                                         AccessFlags  class_flags,
                                         TRAPS) {
  if (class_flags.is_interface()) { // 接口不需要vtable表
    // Interfaces do not use vtables, so there is no point to assigning
    // a vtable index to any of their methods.  If we refrain(克制; 节制; 避免;) from doing this,
    // we can use Method::_vtable_index to hold the itable index
    return false;
  }

  if (target_method->is_final_method(class_flags) || // final方法不需要一个新的entry
      // a final method never needs a new entry; final methods can be statically
      // resolved and they have to be present in the vtable only if they override
      // a super's method, in which case they re-use its entry
      ( target_method()->is_static() ) || // 静态方法不需要一个新的entry
      // static methods don't need to be in vtable
      ( target_method()->name() ==  vmSymbols::object_initializer_name() ) // <init>不会被动态绑定
      // <init> is never called dynamically-bound
  ){
      return false;
  }

  // Concrete interface methods do not need new entries, they override
  // abstract method entries using default inheritance rules
  if (target_method()->method_holder() != NULL &&
      target_method()->method_holder()->is_interface()  &&
      !target_method()->is_abstract() ){
     return false;
  }

  // we need a new entry if there is no superclass
  if (super == NULL) {
     return true;
  }

  // private methods in classes always have a new entry in the vtable
  // specification interpretation since classic has
  // private methods not overriding
  // JDK8 adds private  methods in interfaces which require invokespecial
  if (target_method()->is_private()) {
     return true;
  }

  // 省略对miranda方法的判断逻辑
  return true; // found no match; we need a new entry
}

所有的私有方法都需要vtable entry。还有一类特殊的miranda方法也需要实现“晚绑定”,所以也会有vtable entry。miranda方法是为了解决早期HotSpot虚拟机的一个Bug,因为早期虚拟机在遍历Java类的方法时,只会遍历类及所有父类的方法,不会遍历Java类所实现的接口里的方法,这会导致一个问题,即如果Java类没有实现接口里的方法,那么接口中的方法将不会被遍历到。为了解决这个问题,Javac等前端编译器会向Java类中添加方法,这些方法就是miranda方法。举个例子如下:

public interface IA{
   void test();
}

public abstract class CA implements IA{
    public CA(){
       test();
   }
}

CA类实现了IA接口,但是并没有实现接口中定义的test()方法,源代码并没有任何问题,如果只遍历类及父类,那么是无法查找到test()方法的,所以早期的HotSpot需要Javac等编译器会为CA类合成一个miranda方法,如下: 

public interface IA{
   void test();
}

public abstract class CA implements IA{
    public CA(){
       test();
   }

   // miranda
   public abstract void test();
}

这样就解决了HotSpot不搜索接口的Bug。不过现在的虚拟机版本并不需要合成miranda方法(Class文件中不存在miranda方法),但是在填充类的vtable时,如果这个类实现的接口中有没有被实现的方法,那么仍然需要在vtable中新增vtable entry,其实也是起到了和之前一样的效果。

接着看needs_new_vtable_entry()方法中对miranda方法的判断逻辑,如下:

// search through the super class hierarchy to see if we need a new entry
ResourceMark   rm;
Symbol*        name = target_method()->name();
Symbol*        signature = target_method()->signature();
Klass*         k = super;
Method*        super_method = NULL;
InstanceKlass  *holder = NULL;
Method*        recheck_method =  NULL;
while (k != NULL) { 
    // lookup through the hierarchy for a method with matching name and sign.
    super_method = InstanceKlass::cast(k)->lookup_method(name, signature);
    if (super_method == NULL) {
       break; // we still have to search for a matching miranda method
    }
    // get the class holding the matching method
    // make sure you use that class for is_override
    InstanceKlass* superk = super_method->method_holder();
    // we want only instance method matches
    // pretend private methods are not in the super vtable
    // since we do override around them: e.g. a.m pub/b.m private/c.m pub,
    // ignore private, c.m pub does override a.m pub
    // For classes that were not javac'd together, we also do transitive overriding around
    // methods that have less accessibility
    if (  (!super_method->is_static()) &&
          (!super_method->is_private())   ) { // super_method即不是静态也不是private的
      if (superk->is_override(super_method, classloader, classname, THREAD)) {
         return false;
      // else keep looking for transitive overrides
      }
    }

    // Start with lookup result and continue to search up
    k = superk->super(); // haven't found an override match yet; continue to look
} // end while

// if the target method is public or protected it may have a matching
// miranda method in the super, whose entry it should re-use.
// Actually, to handle cases that javac would not generate, we need
// this check for all access permissions.
InstanceKlass *sk = InstanceKlass::cast(super);
if (sk->has_miranda_methods()) {
    if (sk->lookup_method_in_all_interfaces(name, signature, false) != NULL) {
       return false;  // found a matching miranda; we do not need a new entry
    }
}

调用lookup_method()方法搜索父类中是否有匹配name和signature的方法。如果搜索到方法,那可能是重写的情况,在重写情况下不需要新为此方法增加vtableEntry,只需要更新即可;如果搜索不到也不一定说明需要一个新的vtableEntry,因为还有miranda方法的情况,当调用lookup_method_in_all_interfaces()方法搜索到相关方法时,表示不需要新的vtableEntry,举个例子如下:

interface IA {
	void test();
}

abstract class CA implements IA{ }

public abstract class MirandaTest  extends CA {
	public abstract void test();	
}

在处理MirandaTest类的test()方法时,从CA和Object父类中无法搜索到test()类,但是在处理CA时,由于CA类没有实现IA接口中的test()方法,所以CA类的vtable中含有代表test()方法的vtableEntry,那么MirandaTest类中的test()方法此时就不需要一个新的vtableEntry了,只需要更新即可,所以方法最终返回false。 

方法中的lookup_method()的实现如下: 

Method* lookup_method(Symbol* name, Symbol* signature) const {
    return uncached_lookup_method(name, signature);
}

// uncached_lookup_method searches both the local class methods array and all
// superclasses methods arrays, skipping any overpass methods in superclasses.
Method* InstanceKlass::uncached_lookup_method(Symbol* name, Symbol* signature) const {
  Klass* klass = const_cast<InstanceKlass*>(this);
  bool dont_ignore_overpasses = true;  // For the class being searched, find its overpasses.
  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;  // Ignore overpass methods in all superclasses.
  }
  return NULL;
}

// find_method looks up the name/signature in the local methods array
Method* InstanceKlass::find_method(Symbol* name, Symbol* signature) const {
  return InstanceKlass::find_method(methods(), name, signature);
}

// find_method looks up the name/signature in the local methods array
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()方法就是对methods进行二分算法来搜索名称为name和签名为signature的方法,这里不在介绍。 

调用的is_override()方法的实现如下: 

// Returns true iff super_method can be overridden by a method in targetclassname
// See JSL 3rd edition 8.4.6.1
// Assumes name-signature match
// "this" is InstanceKlass of super_method which must exist
// note that the InstanceKlass of the method in the targetclassname has not always been created yet
bool InstanceKlass::is_override(methodHandle super_method, Handle targetclassloader, Symbol* targetclassname, TRAPS) {
   // Private methods can not be overridden
   if (super_method->is_private()) {
     return false;
   }
   // If super method is accessible, then override
   if ((super_method->is_protected()) ||
       (super_method->is_public())) {
     return true;
   }
   // Package-private methods are not inherited outside of package
   assert(super_method->is_package_private(), "must be package private");
   return(is_same_class_package(targetclassloader(), targetclassname)); // 其中就是处理权限为default的方法
}

调用的lookup_method_in_all_interfaces()方法的实现如下:  

// lookup a method in all the interfaces that this class implements
// Do NOT return private or static methods, new in JDK8 which are not externally visible
// They should only be found in the initial InterfaceMethodRef
Method* InstanceKlass::lookup_method_in_all_interfaces(Symbol* name,
                                                       Symbol* signature,
                                                       bool skip_default_methods) const {
  Array<Klass*>* all_ifs = transitive_interfaces();
  int num_ifs = all_ifs->length();
  InstanceKlass *ik = NULL;
  for (int i = 0; i < num_ifs; i++) {
    ik = InstanceKlass::cast(all_ifs->at(i));
    Method* m = ik->lookup_method(name, signature);
    if (m != NULL && m->is_public() && !m->is_static() &&
        (!skip_default_methods || !m->is_default_method())) {
      return m;
    }
  }
  return NULL;
}

在函数中调用此方法时,skip_default_methods的值为false。逻辑实现比较简单,调用InstanceKlass类的lookup_method()方法查找接口中是否有名称为name、签名为signature的方法,如果查找到了public、非静态方法则直接返回,也就是说父类的vtable中已经存在了名称为name、签名为signature的方法的vtableEntry,所以当前类中并不再需要一个新的vtableEntry。

2、get_mirandas()方法

调用的get_mirandas()方法的实现如下:

void klassVtable::get_mirandas(GrowableArray<Method*>* new_mirandas,
                               GrowableArray<Method*>* all_mirandas,
                               Klass* super, Array<Method*>* class_methods,
                               Array<Method*>* default_methods,
                               Array<Klass*>* local_interfaces) {
  assert((new_mirandas->length() == 0) , "current mirandas must be 0");

  // iterate thru the local interfaces looking for a miranda
  int num_local_ifs = local_interfaces->length();
  for (int i = 0; i < num_local_ifs; i++) {
    InstanceKlass *ik = InstanceKlass::cast(local_interfaces->at(i));
    add_new_mirandas_to_lists(new_mirandas, all_mirandas,
                              ik->methods(), class_methods,
                              default_methods, super);
    // iterate thru each local's super interfaces
    Array<Klass*>* super_ifs = ik->transitive_interfaces();
    int num_super_ifs = super_ifs->length();
    for (int j = 0; j < num_super_ifs; j++) {
      InstanceKlass *sik = InstanceKlass::cast(super_ifs->at(j));
      add_new_mirandas_to_lists(new_mirandas, all_mirandas,
                                sik->methods(), class_methods,
                                default_methods, super);
    }
  }
}

如上方法遍历当前类实现的接口以及接口所继承的所有接口,然后调用add_new_mirandas_to_lists()方法进行处理,此方法的实现如下:

// Scans current_interface_methods for miranda methods that do not
// already appear in new_mirandas, or default methods,  and are also not defined-and-non-private
// in super (superclass).  These mirandas are added to all_mirandas if it is
// not null; in addition, those that are not duplicates of miranda methods
// inherited by super from its interfaces are added to new_mirandas.
// Thus, new_mirandas will be the set of mirandas that this class introduces,
// all_mirandas will be the set of all mirandas applicable to this class
// including all defined in superclasses.
void klassVtable::add_new_mirandas_to_lists(
    GrowableArray<Method*>* new_mirandas,
	GrowableArray<Method*>* all_mirandas,
    Array<Method*>*         current_interface_methods,
	Array<Method*>*         class_methods,
    Array<Method*>*         default_methods,
	Klass*                  super
){

  // iterate thru the current interface's method to see if it a miranda
  int num_methods = current_interface_methods->length();
  for (int i = 0; i < num_methods; i++) {
		Method* im = current_interface_methods->at(i);
		bool is_duplicate = false;
		int num_of_current_mirandas = new_mirandas->length();

		// check for duplicate mirandas in different interfaces we implement
		for (int j = 0; j < num_of_current_mirandas; j++) {
		  Method* miranda = new_mirandas->at(j);
		  if ((im->name() == miranda->name()) &&
			  (im->signature() == miranda->signature()) ){
			is_duplicate = true;
			break;
		  }
		}

		if (!is_duplicate) { // we don't want duplicate miranda entries in the vtable
		  if (is_miranda(im, class_methods, default_methods, super)) { // is it a miranda at all?
			InstanceKlass *sk = InstanceKlass::cast(super);
			// check if it is a duplicate of a super's miranda
			if (sk->lookup_method_in_all_interfaces(im->name(), im->signature(), false) == NULL) {
			    new_mirandas->append(im);
			}
			if (all_mirandas != NULL) {
			    all_mirandas->append(im);
			}
		  }
		}

  } // end for
}

遍历当前类实现的接口(直接或间接)中定义的所有方法,如果这个方法还没有被判定为miranda方法(就是在new_mirandas数组中不存在),那么调用is_miranda()方法判断此方法是否为miranda()方法,如果是,那么还需要调用当前类的父类的lookup_method_in_all_interfaces()方法来进一步判断。举个例子如下:

interface IA {
	void test();
}

abstract class CA implements IA{ }

在处理CA类时,由于CA实现的接口IA中的方法test()没有对应的实现,所以接口中定义的test()方法会添加到new_mirandas数组中,意思就是需要在当前CA类的vtable中添加对应的vtableEntry。再举个例子,如下:

interface IA {
	void test();
}

abstract class CA implements IA{  }

interface IB {
	void test();
}
public abstract class MirandaTest  extends CA  implements IB{
	
}

如果当前类为MirandaTest,那么实现的IB接口中的test()方法没有对应的实现,但是并不一定会添加到new_mirandas数组中,所以也就意味着不一定会新增加vtableEntry,还需要调用lookup_method_in_all_interfaces()方法来判断,由于当前类的父类CA中已经有名称和签名都相等的test()方法对应的vtableEntry了,所以只需要重用此vtableEntry即可。 

调用的is_miranda()方法的实现如下:

// check if a method is a miranda method, given a class's methods table,
// its default_method table  and its super
// Miranda methods are calculated twice:
// first: before vtable size calculation: including abstract and default
// This is seen by default method creation
// Second: recalculated during vtable initialization: only abstract
// This is seen by link resolution and selection.
// "miranda" means not static, not defined by this class.
// private methods in interfaces do not belong in the miranda list.
// the caller must make sure that the method belongs to an interface implemented by the class
// Miranda methods only include public interface instance methods
// Not private methods, not static methods, not default == concrete abstract
// Miranda methods also do not include overpass methods in interfaces
bool klassVtable::is_miranda(Method* m, Array<Method*>* class_methods,
                             Array<Method*>* default_methods, Klass* super) {
  if (m->is_static() || m->is_private() || m->is_overpass()) {
    return false;
  }
  Symbol* name = m->name();
  Symbol* signature = m->signature();

  if (InstanceKlass::find_instance_method(class_methods, name, signature) == NULL) {
    // did not find it in the method table of the current class
    if ((default_methods == NULL) ||
        InstanceKlass::find_method(default_methods, name, signature) == NULL) {
      // 当前类没有父类,那么接口中定义的方法肯定没有对应的实现,此接口中的方法是miranda方法
      if (super == NULL) {
        // super doesn't exist
        return true;
      }

      // 需要从父类中找一个非静态的、名称为name、签名为signauture的方法,如果是静态方法,则
      // 需要继续查找,因为静态方法不参与动态绑定,也就不需要判断是否重写与实现等特性
      Method* mo = InstanceKlass::cast(super)->lookup_method(name, signature);
      while (
                mo != NULL && 
                mo->access_flags().is_static() &&
                mo->method_holder() != NULL &&
                mo->method_holder()->super() != NULL
      ){
         mo = mo->method_holder()->super()->uncached_lookup_method(name, signature);
      }
      // 如果找不到或找到的是私有方法实现,那么说明接口中定义的方法没有对应的实现,此接口中的方法是miranda方法
      if (mo == NULL || mo->access_flags().is_private() ) {
        // super class hierarchy does not implement it or protection is different
        return true;
      }
    }
  }

  return false;
}

接口中的静态、私有等方法一定是非miranda方法,直接返回false。从class_methods数组中查找名称为name、签名为signature的方法,其中的class_methods就是当前分析的类中定义的所有方法,找不到说明没有实现对应的接口中定义的方法,有可能是miranda方法,需要继续进行判断。在判断miranda方法时传入的default_methods为NULL,所以需要继续从父类中判断。如果没有父类或父类中找不到对应的方法实现,那么方法会返回true,表示是miranda方法。 

 

posted on 2020-08-09 15:28  鸠摩(马智)  阅读(707)  评论(0编辑  收藏  举报

导航