• HotSpot的类模型(3)


    上一篇 HotSpot的类模型(2) 介绍了类模型的基础类Klass的重要属性及方法,这一篇介绍一下InstanceKlass及InstanceKlass的子类。

    2、InstanceKlass类

    每个InstanceKlass对象表示一个具体的Java类(这里的Java类不包括Java数组)。InstanceKlass类及重要属性的定义如下:

    class InstanceKlass: public Klass {
     ...
    
     protected:
      // Annotations for this class
      Annotations*       _annotations;
      // Array classes holding elements of this class.
      Klass*             _array_klasses;
      // Constant pool for this class.
      ConstantPool*     _constants;
      // The InnerClasses attribute and EnclosingMethod attribute. The
      // _inner_classes is an array of shorts. If the class has InnerClasses
      // attribute, then the _inner_classes array begins with 4-tuples of shorts
      // [inner_class_info_index, outer_class_info_index,
      // inner_name_index, inner_class_access_flags] for the InnerClasses
      // attribute. If the EnclosingMethod attribute exists, it occupies the
      // last two shorts [class_index, method_index] of the array. If only
      // the InnerClasses attribute exists, the _inner_classes array length is
      // number_of_inner_classes * 4. If the class has both InnerClasses
      // and EnclosingMethod attributes the _inner_classes array length is
      // number_of_inner_classes * 4 + enclosing_method_attribute_size.
      Array<jushort>*   _inner_classes;
     
      // Array name derived from this class which needs unreferencing
      // if this class is unloaded.
      Symbol*           _array_name;
     
      // Number of heapOopSize words used by non-static fields in this klass
      // (including inherited fields but after header_size()).
      int               _nonstatic_field_size;
      int               _static_field_size;    // number words used by static fields (oop and non-oop) in this klass
      // Constant pool index to the utf8 entry of the Generic signature,
      // or 0 if none.
      u2                _generic_signature_index;
      // Constant pool index to the utf8 entry for the name of source file
      // containing this klass, 0 if not specified.
      u2                _source_file_name_index;
      u2                _static_oop_field_count;// number of static oop fields in this klass
      u2                _java_fields_count;    // The number of declared Java fields
      int               _nonstatic_oop_map_size;// size in words of nonstatic oop map blocks
     
    
      u2                _minor_version;  // minor version number of class file
      u2                _major_version;  // major version number of class file
      Thread*           _init_thread;    // Pointer to current thread doing initialization (to handle recusive initialization)
      int               _vtable_len;     // length of Java vtable (in words)
      int               _itable_len;     // length of Java itable (in words)
      OopMapCache*      volatile _oop_map_cache;   // OopMapCache for all methods in the klass (allocated lazily)
      JNIid*            _jni_ids;              // First JNI identifier for static fields in this class
      jmethodID*        _methods_jmethod_ids;  // jmethodIDs corresponding to method_idnum, or NULL if none
      nmethodBucket*    _dependencies;         // list of dependent nmethods
      nmethod*          _osr_nmethods_head;    // Head of list of on-stack replacement nmethods for this class
    
     
      // Class states are defined as ClassState (see above).
      // Place the _init_state here to utilize the unused 2-byte after
      // _idnum_allocated_count.
      u1                _init_state;                    // state of class
      u1                _reference_type;                // reference type
     
    
      // Method array.
      Array<Method*>*   _methods;
      // Default Method Array, concrete methods inherited from interfaces
      Array<Method*>*   _default_methods;
      // Interface (Klass*s) this class declares locally to implement.
      Array<Klass*>*    _local_interfaces;
      // Interface (Klass*s) this class implements transitively.
      Array<Klass*>*    _transitive_interfaces;
    
      // Int array containing the vtable_indices for default_methods
      // offset matches _default_methods offset
      Array<int>*       _default_vtable_indices;
     
      // Instance and static variable information, starts with 6-tuples of shorts
      // [access, name index, sig index, initval index, low_offset, high_offset]
      // for all fields, followed by the generic signature data at the end of
      // the array. Only fields with generic signature attributes have the generic
      // signature data set in the array. The fields array looks like following:
      //
      // f1: [access, name index, sig index, initial value index, low_offset, high_offset]
      // f2: [access, name index, sig index, initial value index, low_offset, high_offset]
      //      ...
      // fn: [access, name index, sig index, initial value index, low_offset, high_offset]
      //     [generic signature index]
      //     [generic signature index]
      //     ...
      Array<u2>*        _fields;
     
      // embedded Java vtable follows here
      // embedded Java itables follows here
      // embedded static fields follows here
      // embedded nonstatic oop-map blocks follows here
      // embedded implementor of this interface follows here
      //   The embedded implementor only exists if the current klass is an
      //   iterface. The possible values of the implementor fall into following
      //   three cases:
      //     NULL: no implementor.
      //     A Klass* that's not itself: one implementor.
      //     Itsef: more than one implementors.
      // embedded host klass follows here
      //   The embedded host klass only exists in an anonymous class for
      //   dynamic language support (JSR 292 enabled). The host class grants
      //   its access privileges to this class also. The host class is either
      //   named, or a previously loaded anonymous class. A non-anonymous class
      //   or an anonymous class loaded through normal classloading does not
      //   have this embedded field.
      
      ...
    }
    

    重要属性的介绍如下表所示。

    字段名 作用
    _annotations Annotations类型的指针,保存该类使用的所有注解
    _array_klasses

    数组元素为该类的数组Klass指针,例如ObjArrayKlass是对象数组且元素类型为Object,

    那么表示Object类的InstanceKlass对象的_array_klasses就是指向ObjArrayKlass的指针

    _array_name

    以该类为数组元素的数组的名字,如"[Ljava/lang/Object;"

    _constants ConstantPool类型的指针,用来保存类的常量池信息
    _inner_classes 用一个jushort数组保存当前类的InnerClasses属性和EnclosingMethod属性
    _nonstatic_field_size 非静态字段需要占用的内存大小 ,以字为单位
    _static_field_size 静态字段需要占用的内存大小 ,以字为单位
    _generic_signature_index

    保存此类的Generic signature在常量池中的索引

    _source_file_name_index 保存此类的源文件名在常量池中索引
    _static_oop_field_count 此类包含的静态引用类型字段的数量
    _java_fields_count 字段总数量
    _nonstatic_oop_map_size 非静态的oop map block的内存大小,以字为单位
    _minor_version 类的次版本号
    _major_version 类的主版本号
    _init_thread 执行此类初始化的Thread指针
    _vtable_len Java虚函数表(vtable)所占用的内存大小,以字为单位
    _itable_len Java接口函数表(itable)所占用的内存大小,以字为单位
    _oop_map_cache OopMapCache指针,该类的所有方法的OopMapCache
    _jni_ids/_methods_jmethod_ids JNIid指针与jmethodID指针,这2个指针对于JNI方法操作属性和方法非常重要,在介绍JNI时会详细介绍。
    _dependencies nmethodBucket指针,依赖的本地方法,以根据其_next属性获取下一个nmethod
    _osr_nmethods_head 栈上替换的本地方法链表的头元素
    _init_state

    表示类的状态,为枚举类型ClassState,定义了如下常量值:

    • allocated(已分配内存)
    • loaded(从class文件读取加载到内存中)
    • linked(已经成功链接和校验)
    • being_initialized(正在初始化)
    • fully_initialized(已经完成初始化)
    • initialization_error(初始化异常)
    _reference_type 引用类型
    _methods 保存方法的指针数组
    _default_methods 保存方法的指针数组,从接口继承的默认方法
    _local_interfaces 保存接口的指针数组,直接实现的接口Klass
    _transitive_interfaces 保存接口的指针数组,包含_local_interfaces和通过继承间接实现的接口
    _default_vtable_indices 默认方法在虚函数表中的索引
    _fields

    类的字段属性,每个字段的6个属性access,、name index、sig index、initial value index、low_offset、high_offset组成一个元组,

    access表示访问控制属性,根据name index可以获取属性名,根据initial value index可以获取初始值,根据low_offset与

    high_offset可以获取该属性在内存中的偏移量。另外保存完所有属性之后还可能会保存泛型签名信息。

    有了InstanceKlass与Klass中定义的这些属性足够用来保存Java类元信息。在后续的类解析中会看到对相关变量的属性填充操作。除了保存类元信息外,此类还有另外一个重要的功能,即支持方法分派,主要是通过Java虚方法表和Java接口函数表来完成的,不过C++并不像Java一样,保存信息时非要在类中定义出相关属性,C++只是在分配内存时为要存储的信息分配好特定的内存,然后直接通过内存偏移来操作即可。

    接下来几个属性是没有对应的属性名,只能通过指针和偏移量的方式访问:

    • Java vtable:Java虚函数表,大小等于_vtable_len;
    • Java itables:Java接口函数表,大小等于 _itable_len;
    • 非静态oop-map blocks ,大小等于_nonstatic_oop_map_size;
    • 接口的实现类,只有当前类表示一个接口时存在。如果接口没有任何实现类则为NULL;如果只有一个实现类则为该实现类的Klass指针;如果有多个实现类,为当前类本身;
    • host klass,只在匿名类中存在,为了支持JSR 292中的动态语言特性,会给匿名类生成一个host klass。 

    HotSpot在解析一个类时会调用InstanceKlass::allocate_instance_klass()方法分配内存,而分配多大的内存则是通过调用InstanceKlass::size()计算出来的,调用语句如下: 

      int size = InstanceKlass::size(vtable_len,
    								 itable_len,
    								 nonstatic_oop_map_size,
    		                         isinterf,
    								 is_anonymous);
    

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

    static int size(int vtable_length,
    		          int itable_length,
                      int nonstatic_oop_map_size,
                      bool is_interface,
    				  bool is_anonymous
      ){
        return align_object_size(header_size()    +  // header_size()为55
               align_object_offset(vtable_length) +
               align_object_offset(itable_length) +
               (
                      (is_interface || is_anonymous) ?
                      align_object_offset(nonstatic_oop_map_size) :
                      nonstatic_oop_map_size
    		   ) +
               (
            		   is_interface ? (int)sizeof(Klass*)/HeapWordSize : 0
               ) +
               (
            		   is_anonymous ? (int)sizeof(Klass*)/HeapWordSize : 0)
    		   );
      }

    可以看到除了会为类中本身的属性分配内存,也会为vtable与itable等分配内存。调用的header_size()方法就是计算此类的对象所占用的内存大小,实现如下:

    // Sizing (in words) 
    static int header_size(){ 
      return align_object_offset(sizeof(InstanceKlass)/HeapWordSize); // 以HeapWordSize为单位,64位一个字为8字节,所以值为8 
    }
    

    调用的align_object_offset()方法是进行内存对齐,这是一块非常重要的C++知识点,后面会专门进行讲解。 

    3、InstanceKlass类的子类

    InstanceKlass共有3个直接子类,这3个子类用来表示一些特殊的类,下面简单介绍一下这3个子类:

    (1)InstanceRefKlass

    java/lang/ref/Reference的子类需要使用InstanceRefKlass类来表示,因为这些类需要垃圾回收器特殊处理 ,在后续讲解强引用、弱引用、虚引用以及幽灵引用时在详细介绍。

    (2)InstanceMirrorKlass 

    用于表示特殊的java.lang.Class类,我们需要分清相关类的表示方法,如下图所示。

    java.lang.Class对象是通过对应的Oop对象来保存类的静态属性,因此他们的实例大小不同,需要特殊的方式来计算他们的大小以及属性遍历。

    Klass的属性_java_mirror就指向保存该类静态字段的Oop对象,可通过该属性访问类的静态字段。 Oop是HotSpot的对象表示模型,在后面会详细介绍。

     (3)InstanceClassLoaderKlass 

    没有添加新的字段,增加了新的oop遍历方法,主要用于类加载器依赖遍历使用。  

    创建InstanceKlass实例会调用InstanceKlass::allocate_instance_klass()方法。在创建时,会涉及到C++new运算符的重载,通过重载new运算符来分配对象的内存空间,也就是调用InstanceKlass::size()方法得到的大小,然后再调用对应类的构造函数初始化相应的属性。方法的实现如下:

    InstanceKlass* InstanceKlass::allocate_instance_klass(
                                                  ClassLoaderData* loader_data,
                                                  int vtable_len,
                                                  int itable_len,
                                                  int static_field_size,
                                                  int nonstatic_oop_map_size,
                                                  ReferenceType rt,
                                                  AccessFlags access_flags,
                                                  Symbol* name,
                                                  Klass* super_klass,
                                                  bool is_anonymous,
                                                  TRAPS) {
      bool isinterf = access_flags.is_interface();
      int size = InstanceKlass::size(vtable_len,
    								 itable_len,
    								 nonstatic_oop_map_size,
    		                         isinterf,
    								 is_anonymous);
    
      // Allocation
      InstanceKlass* ik;
      ///////////////////////////////////////////////////////////////////////
      if (rt == REF_NONE) {
        if (name == vmSymbols::java_lang_Class()) {
          ik = new (loader_data, size, THREAD) InstanceMirrorKlass(
                           vtable_len,
    					   itable_len,
    					   static_field_size,
    					   nonstatic_oop_map_size,
    					   rt,
                           access_flags, is_anonymous);
        } else if (
        	  name == vmSymbols::java_lang_ClassLoader() ||
              (
                 SystemDictionary::ClassLoader_klass_loaded() &&
                 super_klass != NULL &&
                 super_klass->is_subtype_of(SystemDictionary::ClassLoader_klass()) // ClassLoader_klass为java_lang_ClassLoader
    		  )
        ){
          ik = new (loader_data, size, THREAD) InstanceClassLoaderKlass(
                           vtable_len,
    					   itable_len,
    					   static_field_size,
    					   nonstatic_oop_map_size,
    					   rt,
                           access_flags, is_anonymous);
        } else {
          // normal class
          ik = new (loader_data, size, THREAD) InstanceKlass(
    				vtable_len, itable_len,
    				static_field_size,
    				nonstatic_oop_map_size,
    				rt,
    				access_flags, is_anonymous);
        }
      }
      ///////////////////////////////////////////////////////////////////////
      else {
        // reference klass
        ik = new (loader_data, size, THREAD) InstanceRefKlass(
    				vtable_len, itable_len,
    				static_field_size,
    				nonstatic_oop_map_size,
    				rt,
    				access_flags, is_anonymous);
      }
      ///////////////////////////////////////////////////////////////////////
    
      // 添加所有类型到我们内部类加载器列表中,包括在根加载器中的类
      // Add all classes to our internal class loader list here,
      // including classes in the bootstrap (NULL) class loader.
      // loader_data的类型为ClassLoaderData*,通过ClassLoaderData中的_klasses保持通过InstanceKlass._next_link属性保持的列表
      loader_data->add_class(ik);
    
      return ik;
    }
    

    方法的实现比较简单,当rt等于REF_NONE时,也就是为非Reference类型时,会根据类名创建对应C++类的对象。Class类创建InstanceMirrorKlass、ClassLoader类或ClassLoader的子类创建InstanceClassLoaderKlass类、普通类通过InstanceKlass来表示。当rt不为REF_NONE时,会创建InstanceRefKlass对象。REF_NONE枚举常量的定义如下: 

    // ReferenceType is used to distinguish between java/lang/ref/Reference subclasses
    
    enum ReferenceType {
      REF_NONE,      // Regular class
      REF_OTHER,     // Subclass of java/lang/ref/Reference, but not subclass of one of the classes below
      REF_SOFT,      // Subclass of java/lang/ref/SoftReference
      REF_WEAK,      // Subclass of java/lang/ref/WeakReference
      REF_FINAL,     // Subclass of java/lang/ref/FinalReference
      REF_PHANTOM    // Subclass of java/lang/ref/PhantomReference
    };
    

    可以看到,所有的Reference及子类都会用InstanceRefKlass来表示。当无法判断到底是哪个子类时,会将Reference设置为REF_OTHER。  

    相关文章的链接如下:

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

    2、调试HotSpot源代码

    3、HotSpot项目结构 

    4、HotSpot的启动过程 

    5、HotSpot二分模型(1)

    6、HotSpot的类模型(2)  

    7、HotSpot的类模型(3) 

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

  • 相关阅读:
    基于阿里云GPU云服务器的AIACC助力UC搜索业务性能提效380%,每年节省数千万成本
    如何在 ACK 中使用 MSE Ingress
    面向对象分析与设计的底层逻辑
    当 AI 邂逅绘画艺术,能迸发出怎样的火花?
    是什么让一段20行代码的性能提升了10倍
    我,PolarDB云原生数据库,5年来实现这些重磅技术创新
    一图看懂天河文链如何实现云原生架构升级
    首次全面解析云原生成熟度模型:解决企业「诊断难、规划难、选型难」问题
    C++快速初始化vector
    Grid布局指定列数时,行高自适应为元素高度
  • 原文地址:https://www.cnblogs.com/mazhimazhi/p/13252983.html
Copyright © 2020-2023  润新知