• 计算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方法。 

    相关文章的链接如下:

    1、在Ubuntu 16.04上编译OpenJDK8的源代码 

    2、调试HotSpot源代码

    3、HotSpot项目结构 

    4、HotSpot的启动过程 

    5、HotSpot二分模型(1)

    6、HotSpot的类模型(2)  

    7、HotSpot的类模型(3) 

    8、HotSpot的类模型(4)

    9、HotSpot的对象模型(5)  

    10、HotSpot的对象模型(6) 

    11、操作句柄Handle(7)

    12、句柄Handle的释放(8)

    13、类加载器 

    14、类的双亲委派机制 

    15、核心类的预装载

    16、Java主类的装载  

    17、触发类的装载  

    18、类文件介绍 

    19、文件流 

    20、解析Class文件 

    21、常量池解析(1) 

    22、常量池解析(2)

    23、字段解析(1)

    24、字段解析之伪共享(2) 

    25、字段解析(3)  

    26、字段解析之OopMapBlock(4)

    27、方法解析之Method与ConstMethod介绍  

    28、方法解析

    29、klassVtable与klassItable类的介绍  

    作者持续维护的个人博客classloading.com

    关注公众号,有HotSpot源码剖析系列文章!

       

  • 相关阅读:
    springsecurity-用户注销
    springsecurity-自定义403页面
    springsecurity-用户授权 (注解使用)
    springsecurity-用户授权
    springsecurity-自定义登录页面和自定义认证
    springsecurity-查询数据库认证
    springsecurity-基本原理(过滤器链)
    springsecurity-web权限方案-用户认证(设置用户名和密码)
    u-boot移植(九)---代码修改---NAND
    u-boot移植(八)---代码修改---存储控制器--MMU
  • 原文地址:https://www.cnblogs.com/mazhimazhi/p/13461949.html
Copyright © 2020-2023  润新知