zoukankan      html  css  js  c++  java
  • HotSpot类模型之ArrayKlass

    上一篇分析了 HotSpot类模型之InstanceKlass ,这次主要分析表示java数组类型的C++类。

    1、ArrayKlass

    ArrayKlass继承自Klass,是所有数组类的抽象基类,类及重要属性的定义如下:  

    class ArrayKlass: public Klass {
      ...
     private:
      int               _dimension;         // This is n'th-dimensional array.
      Klass* volatile   _higher_dimension;  // Refers the (n+1)'th-dimensional array (if present).
      Klass* volatile   _lower_dimension;   // Refers the (n-1)'th-dimensional array (if present).
      int               _vtable_len;        // size of vtable for this klass
      oop               _component_mirror;  // component type, as a java/lang/Class
      ...
    }

    在Klass的基础上增加的属性如下表所示。

    字段 作用
    _dimension int类型,表示数组的维度,记为n
    _higher_dimension Klass指针,表示对n+1维数组Klass的引用
    _lower_dimension Klass指针,表示对n-1维数组Klass的引用
    _vtable_len int类型, 虚函数表的长度
    _component_mirror oop, 数组元素对应的java.lang.Class对象的Oop

    _vtable_len的值为5,因为数组是引用类型,父类为Object类,而Object类中有5个虚方法可被用来继承和重写,如下:

    void          finalize()
    boolean       equals(Object)
    String        toString()
    int           hashCode()
    Object        clone()

     _dimension、_higher_dimension与_lower_dimension对于一维及多维数组的描述非常重要,属性值的设置相对简单,这里不在介绍。

    2、ArrayKlass类的子类

    (1)TypeArrayKlass类

    TypeArrayKlass是ArrayKlass的子类,用于表示数组元素是基本类型的数组

    class TypeArrayKlass : public ArrayKlass {
      ...
     private:
      jint _max_length;  // maximum number of elements allowed in an array
      ...
    }

     _max_length表示该数组允许的最大长度。

    数组类和普通类不同,数组类没有对应的Class文件,所以数组类是直接被虚拟机创建的。HotSpot在初始化时就会创建好8个基本类型的一维数组对象TypeArrayKlass。之前在讲解HotSpot启动时讲到过,调用initializeJVM()方法初始化HotSpot,这个方法会最终调用到Universe::genesis()方法,在这个方法中初始化基本类型的一维数组对象TypeArrayKlass。例如初始化boolean类型的一维数组,调用语句如下: 

    _boolArrayKlassObj = TypeArrayKlass::create_klass(T_BOOLEAN, sizeof(jboolean), CHECK);
    

    其中_boolArrayKlassObj是声明在universe.cpp文件中的全局变量,如下:

    Klass* Universe::_boolArrayKlassObj = NULL;
    

    调用TypeArrayKlass::create_klass()方法创建TypeArrayKlass对象,具体就是调用TypeArrayKlass::create_klass()方法来完成,方法的实现如下:

    TypeArrayKlass* TypeArrayKlass::allocate(ClassLoaderData* loader_data, BasicType type, Symbol* name, TRAPS) {
    
      int x = TypeArrayKlass::header_size();
      int size = ArrayKlass::static_size(x);
      // 调用的构造函数在下面
      return new (loader_data, size, THREAD) TypeArrayKlass(type, name);
    }
    

    非常类似于InstanceKlass等对象的创建,首先获取需要内存的大小size,然后通过重载new运算符完成对象内存分配后,调用TypeArrayKlass的构造函数初始化一些属性。

    TypeArrayKlass的header_size()及static_size()函数的实现如下:

    static int header_size(){
    	  int k = sizeof(TypeArrayKlass);
    	  return k/HeapWordSize;
    }
    
    int ArrayKlass::static_size(int header_size) {
      // size of an array klass object
      assert(header_size <= InstanceKlass::header_size(), "bad header size");
      // If this assert fails, see comments in base_create_array_klass.
      header_size = InstanceKlass::header_size();  // 为什么是InstanceKlass的大小??看ArrayKlass::start_of_vtable()函数有说明
      int vtable_len = Universe::base_vtable_size(); //  值为5
    
      int size = header_size + align_object_offset(vtable_len); // 对vtable_len进行对齐操作
    
      return align_object_size(size);
    }
    
    static int header_size(){
       return align_object_offset(sizeof(InstanceKlass)/HeapWordSize);
    }
    

    注意header_size属性的值应该是TypeArrayKlass这个类自身占用的内存大小,但是现在却取的是InstanceKlass这个类自身占用内存的大小。这是因为InstanceKlass占用内存大小比TypeArrayKlass大,有足够内存存放相关数据,更重要的是为了统一从固定的偏移位置取出vtable_len属性的值。这样在实际操作过程中,无需关心是数组还是类,都直接偏移固定位置后取vtable_len属性值即可。 

    TypeArrayKlass的构造函数如下:

    TypeArrayKlass::TypeArrayKlass(BasicType type, Symbol* name) : ArrayKlass(name) {
      int lh = array_layout_helper(type);
      set_layout_helper(lh);
      assert(oop_is_array(), "sanity");
      assert(oop_is_typeArray(), "sanity");
    
      set_max_length(arrayOopDesc::max_array_length(type)); // 设置数组的最大长度
      ...
    }
    

    下面详细介绍一下对_layout_helper属性的设置。调用Klass::array_layout_helper()方法获取_layout_helper属性的值

    jint Klass::array_layout_helper(BasicType etype) {
      assert(etype >= T_BOOLEAN && etype <= T_OBJECT, "valid etype");
      // Note that T_ARRAY is not allowed here.
      int  hsize = arrayOopDesc::base_offset_in_bytes(etype); // hsize表示数组元素的对象头部大小
      int  esize = type2aelembytes(etype); // 对应类型存储所需要的字节数
      bool isobj = (etype == T_OBJECT);
      int  tag   =  isobj ? _lh_array_tag_obj_value : _lh_array_tag_type_value;
      int  esz = exact_log2(esize);
      int  lh = array_layout_helper(tag, hsize, etype, esz);
      
    
      return lh;
    }
    

    关于_layout_helper在之前已经介绍过,由于T_BOOLEAN为基本类型,所以tag的值取0xC0;hsize调用arrayOopDesc::base_offset_in_bytes()方法获取,值为16,后面在讲解arrayOopDesc时会介绍,数组对象其实是由对象头、对象字段数据和对齐填充组成,而这里获取的就是对象头的大小;esize表示对应类型存储所需要的字节数,对于T_BOOLEAN来说,只需要1个字节即可,所以esz为0。最后调用array_layout_helper()方法按照约定组合成一个int类型的数字即可。array_layout_helper()方法的实现如下:

     static jint array_layout_helper(jint tag, int hsize, BasicType etype, int log2_esize) {
        return (tag        << _lh_array_tag_shift)          // 左移30位
          |    (hsize      << _lh_header_size_shift)        // 左移16位
          |    ((int)etype << _lh_element_type_shift)       // 左移1位
          |    (log2_esize << _lh_log2_element_size_shift); // 左移0位
      }

    另外还有对_component_mirror属性的设置。对于一维基本类型的数组来说,这个值是java.lang.Class对象。Class对象使用oop对象来表示,调用java_lang_Class::create_basic_type_mirror()方法获取_component_mirror属性的值,通过java_lang_Class::create_mirror()方法完成属性的设置。例如获取boolean类型的属性值,调用语句如下:

    void Universe::initialize_basic_type_mirrors(TRAPS) {
       ...
       _bool_mirror = java_lang_Class::create_basic_type_mirror("boolean",T_BOOLEAN, CHECK);
       ...
    }
    

    方法create_basic_type_mirror()的实现如下:

    oop java_lang_Class::create_basic_type_mirror(const char* basic_type_name, BasicType type, TRAPS) {
      // This should be improved by adding a field at the Java level or by
      // introducing a new VM klass (see comment in ClassFileParser)
      oop java_class = InstanceMirrorKlass::cast(SystemDictionary::Class_klass())->allocate_instance(NULL, CHECK_0);
      if (type != T_VOID) {
          Klass* aklass = Universe::typeArrayKlassObj(type);
          assert(aklass != NULL, "correct bootstrap");
          set_array_klass(java_class, aklass); // 设置表示基本类型数组的TypeArrayKlass的
      }
      return java_class;
    }
    

    通过InstanceMirrorKlass对象(表示java.lang.Class类)来创建oop(表示java.lang.Class对象),_component_mirror最终设置的就是这个oop。引用类型组成的一维或多维数组的基本元素可以使用Klass对象来表示,如对于下面即将要介绍的Object[]来说,元素类型为Object,所以可以使用InstanceKlass来表示;基本类型组成的一维或多维数组的基本元素没有对应的Klass对象,所以只能使用Class对象来描述boolean、int等类型,这样就会与表示Class对象的oop对象产生关系,相关属性最终的值如下所示。

    TypeArrayKlass._component_mirror=oop
    
    oop._array_klass_offset=TypeArrayKlass

    oop表示java.lang.Class对象,用来描述Java类(包括数组类),而TypeArrayKlass也用来描述Java类(包括数组类),那么2者之间必须会的联系。可以通过_component_mirror属性(和_array_klass_offset属性找到对方,属性的设置过程在在java_lang_Class::create_mirror()函数中进行。

    其它的属性设置很简单,这里不在介绍。 

    (2)ObjArrayKlass类

    ObjArrayKlass是ArrayKlass的子类,用于表示数组元素是类或者数组

    class ObjArrayKlass : public ArrayKlass {
      ...
     private:
      Klass* _element_klass;            // The klass of the elements of this array type
      Klass* _bottom_klass;             // The one-dimensional type (InstanceKlass or TypeArrayKlass)
      ...
    }
    

    该类新增了2个属性,如下:

    •  _element_klass:数组元素对应的Klass对象,如果是多维数组,对应数组元素的ObjArrayKlass对象
    •  _bottom_klass:一维数组的类型,可以是InstanceKlass或者TypeArrayKlass。一维基本类型数组为TypeArrayKlass,而二维基本类型数组就会使用ObjArrayKlass来表示,所以其_bottom_klass会是TypeArrayKlass。 

    HotSpot在Universe::genesis()方法中创建Object数组,如下: 

    InstanceKlass* ik = InstanceKlass::cast(SystemDictionary::Object_klass());
    _objectArrayKlassObj = ik->array_klass(1, CHECK); // 调用表示Object类的InstanceKlass类的array_klass()方法
    

    调用array_klass()方法时传递的参数1表示创建一维数组。调用表示Object类的InstanceKlass对象的方法创建的,所以Object数组的创建要依赖于InstanceKlass对象(表示Object类)进行创建。

    传递的参数1表示创建Object的一维数组类型,array_klass()函数及调用的相关函数的实现如下:

    // array class with specific rank
    Klass* array_klass(int rank, TRAPS)         {
    	  return array_klass_impl(false, rank, THREAD);
    }
    
    Klass* InstanceKlass::array_klass_impl(bool or_null, int n, TRAPS) {
      instanceKlassHandle  this_oop(THREAD, this);
      return array_klass_impl(this_oop, or_null, n, THREAD);
    }
    
    Klass* InstanceKlass::array_klass_impl(instanceKlassHandle this_oop, bool or_null, int n, TRAPS) {
      if (this_oop->array_klasses() == NULL) { // 获取_array_klasses属性的值
        if (or_null){
        	return NULL;
        }
        ResourceMark rm;
        JavaThread *jt = (JavaThread *)THREAD;
        {
          // Atomic creation of array_klasses
          MutexLocker mc(Compile_lock, THREAD);   // for vtables
          MutexLocker ma(MultiArray_lock, THREAD);
          // Check if update has already taken place
          if (this_oop->array_klasses() == NULL) {
        	 ClassLoaderData* CLD = this_oop->class_loader_data();
             Klass*  k = ObjArrayKlass::allocate_objArray_klass(CLD, 1, this_oop, CHECK_NULL);
             this_oop->set_array_klasses(k); // 设置InstanceKlass::_array_klasses属性的值
          }
        }
      }
      // _this will always be set at this point
      ObjArrayKlass* oak = (ObjArrayKlass*)this_oop->array_klasses(); // 获取InstanceKlass::_array_klasses属性的值
      if (or_null) {
        return oak->array_klass_or_null(n);
      }
      return oak->array_klass(n, CHECK_NULL); // 在创建出一维的引用类型数组后,接着创建n维的引用类型数组
    }

    首次创建ObjTypeKlass时,InstanceKlass::_array_klasses属性的值为NULL,这样就会调用objArrayKlass::allocate_objArray_klass()函数,创建出一维的引用类型数组并保存到了InstanceKlass::_array_klasses属性中。有了一维的引用类型数组后就可以接着调用array_klass()方法创建n维的引用类型数组了。

    (1)创建一维引用类型数组ObjArrayKlass::allocate_objArray_klass()

    Klass* ObjArrayKlass::allocate_objArray_klass(
    	ClassLoaderData*   loader_data,
    	int                n,
    	KlassHandle        element_klass,
    	TRAPS
    ) {
      // Eagerly allocate the direct array supertype.
      KlassHandle  super_klass = KlassHandle();
      if (!Universe::is_bootstrapping() || SystemDictionary::Object_klass_loaded()) {
        KlassHandle element_super (THREAD, element_klass->super());
        if (element_super.not_null()) { // element_super是Object,Object的父类是null
          // The element type has a direct super.  E.g., String[] has direct super of Object[].
          super_klass = KlassHandle(THREAD, element_super->array_klass_or_null());
          bool supers_exist = super_klass.not_null();
          // Also, see if the element has secondary supertypes.
          // We need an array type for each.
          Array<Klass*>* element_supers = element_klass->secondary_supers();
          for( int i = element_supers->length()-1; i >= 0; i-- ) {
            Klass* elem_super = element_supers->at(i);
            if (elem_super->array_klass_or_null() == NULL) {
                supers_exist = false;
                break;
            }
          }
          if (!supers_exist) {
            // Oops.  Not allocated yet.  Back out, allocate it, and retry.
            KlassHandle ek;
            {
              MutexUnlocker mu(MultiArray_lock);
              MutexUnlocker mc(Compile_lock);   // for vtables
              Klass* sk = element_super->array_klass(CHECK_0);
              super_klass = KlassHandle(THREAD, sk);
              for( int i = element_supers->length()-1; i >= 0; i-- ) {
                  KlassHandle  elem_super(THREAD, element_supers->at(i));
                  elem_super->array_klass(CHECK_0);
              }
              // Now retry from the beginning
              Klass* klass_oop = element_klass->array_klass(n, CHECK_0);
              // Create a handle because the enclosing brace, when locking
              // can cause a gc.  Better to have this function return a Handle.
              ek = KlassHandle(THREAD, klass_oop);
            }  // re-lock
            return ek();
          }
        } else { // element_super不是Object
            // The element type is already Object.  Object[] has direct super of Object.
            super_klass = KlassHandle(THREAD, SystemDictionary::Object_klass());
        }
      }
    
      // Create type name for klass.
      Symbol* name = NULL;
      if ( !element_klass->oop_is_instance() ||
           (name = InstanceKlass::cast(element_klass())->array_name()) == NULL
      ){
        ResourceMark rm(THREAD);
        char *name_str = element_klass->name()->as_C_string();
        int len = element_klass->name()->utf8_length();
        char *new_str = NEW_RESOURCE_ARRAY(char, len + 4);
        int idx = 0;
        new_str[idx++] = '[';
        if (element_klass->oop_is_instance()) { // it could be an array or simple type
           new_str[idx++] = 'L';
        }
        memcpy(&new_str[idx], name_str, len * sizeof(char));
        idx += len;
        if (element_klass->oop_is_instance()) {
           new_str[idx++] = ';';
        }
        new_str[idx++] = '';
        name = SymbolTable::new_permanent_symbol(new_str, CHECK_0);
        if (element_klass->oop_is_instance()) {
          InstanceKlass* ik = InstanceKlass::cast(element_klass());
          ik->set_array_name(name);// 设置InstanceKlass::_array_name的属性
        }
      }
    
      // Initialize instance variables
      ObjArrayKlass* oak = ObjArrayKlass::allocate(loader_data, n, element_klass, name, CHECK_0);
    
      // Add all classes to our internal class loader list here,
      // including classes in the bootstrap (NULL) class loader.
      // GC walks these as strong roots.
      loader_data->add_class(oak);
    
      // Call complete_create_array_klass after all instance variables has been initialized.
      ArrayKlass::complete_create_array_klass(oak, super_klass, CHECK_0);
    
      return oak;
    }

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

    ObjArrayKlass* ObjArrayKlass::allocate(ClassLoaderData* loader_data, int n, KlassHandle klass_handle, Symbol* name, TRAPS) {
      assert(ObjArrayKlass::header_size() <= InstanceKlass::header_size(),"array klasses must be same size as InstanceKlass");
      int x = ObjArrayKlass::header_size();
      int size = ArrayKlass::static_size(x);
    
      return new (loader_data, size, THREAD) ObjArrayKlass(n, klass_handle, name);
    }
    
    int ArrayKlass::static_size(int header_size) {
      // size of an array klass object
      assert(header_size <= InstanceKlass::header_size(), "bad header size");
      // If this assert fails, see comments in base_create_array_klass.
      header_size = InstanceKlass::header_size();  // 为什么是InstanceKlass的大小??看ArrayKlass::start_of_vtable()函数有说明
      int    vtable_len = Universe::base_vtable_size(); //  值为5
      int    size = header_size + align_object_offset(vtable_len); // 对vtable_len进行对齐操作
      return align_object_size(size);
    }
    

    ArrayKlass::complete_create_array_klass()函数的实现如下:

    // Initialization of vtables and mirror object is done separatly from base_create_array_klass,
    // since a GC can happen. At this point all instance variables of the ArrayKlass must be setup.
    void ArrayKlass::complete_create_array_klass(ArrayKlass* k, KlassHandle super_klass, TRAPS) {
      ResourceMark rm(THREAD);
    
      Klass*  curr_superklass = super_klass(); // super_klass是个参数,类型为KlassHandle
      k->initialize_supers(curr_superklass, CHECK);
    
      klassVtable* kv = k->vtable();
      kv->initialize_vtable(false, CHECK); // 会初始化当前Klass的vtable(含有_length个vtableEntry)
    
      java_lang_Class::create_mirror(k, Handle(NULL), CHECK);
    }

    调用initialize_vtalbe()完成虚函数表的初始化,调用java_lang_Class::create_mirror()函数完成当前ObjTypeArray对象对应的java.lang.Class对象的创建并设置了相关属性。

    (2)创建n维引用类型数组ObjArrayKlass::array_klass()

    Klass* ObjArrayKlass::array_klass_impl(bool or_null, int n, TRAPS) {
    
      assert(dimension() <= n, "check order of chain");
      int dim = dimension();
      if (dim == n)
    	  return this;
    
      if (higher_dimension() == NULL) {
        if (or_null)
        	return NULL;
    
        ResourceMark rm;
        JavaThread *jt = (JavaThread *)THREAD;
        {
          MutexLocker mc(Compile_lock, THREAD);   // for vtables
          // Ensure atomic creation of higher dimensions
          MutexLocker mu(MultiArray_lock, THREAD);
    
          // Check if another thread beat us
          if (higher_dimension() == NULL) {
    
            // Create multi-dim klass object and link them together
            Klass* k = ObjArrayKlass::allocate_objArray_klass(class_loader_data(), dim + 1, this, CHECK_NULL);
            ObjArrayKlass* ak = ObjArrayKlass::cast(k);
            ak->set_lower_dimension(this);
            OrderAccess::storestore();
            set_higher_dimension(ak);
            assert(ak->oop_is_objArray(), "incorrect initialization of ObjArrayKlass");
          }
        }
      } else {
        CHECK_UNHANDLED_OOPS_ONLY(Thread::current()->clear_unhandled_oops());
      }
    
    
      ObjArrayKlass *ak = ObjArrayKlass::cast(higher_dimension());
      if (or_null) {
        return ak->array_klass_or_null(n);
      }
      return ak->array_klass(n, CHECK_NULL);
    }
    
    Klass* ObjArrayKlass::array_klass_impl(bool or_null, TRAPS) {
      return array_klass_impl(or_null, dimension() +  1, CHECK_NULL);
    }
    

    最终表示Object类的InstanceKlass与表示一维数组Object[]的ObjArrayKlass之间的相关属性如下:

    ObjArrayKlass._element_klass=InstanceKlass
    ObjArrayKlass._bottom_klass=InstanceKlass 
    
    InstanceKlass._array_name="[Ljava/lang/Object;"
    InstanceKlass._array_klasses=ObjArrayKlass
    

    ObjArrayKlass中其它的属性设置也并不复杂,这里不在介绍。

    其它参考文章:

    1、在Ubuntu 16.04上编译OpenJDK8的源代码(配视频)  

    2、调试HotSpot源代码(配视频)

    3、HotSpot项目结构

    4、HotSpot的启动过程(配视频进行源码分析)

    5、HotSpot源码分析之C++对象的内存布局

    6、HotSpot源码分析之类模型

    7、HotSpot类模型之InstanceKlass

    搭建过程中如果有问题可直接评论留言或加作者微信mazhimazh。

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

    B站上有HotSpot源码分析相关视频 https://space.bilibili.com/27533329

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

      

  • 相关阅读:
    spring boot actuator监控需要注意的点
    spring boot actuator端点高级进阶metris指标详解、git配置详解、自定义扩展详解
    3、尚硅谷_SSM高级整合_创建Maven项目.avi
    elasticSearch插件metricbeat收集nginx的度量指标
    elasticSearch插件的安装以及使用nginx的modles收集nginx的日志
    服务治理平台微服务介绍
    skywalking面板功能介绍2
    skywalking中表字段的信息
    js总结33 :javascript-DOM节点属性
    js教程系列32 :javascript-DOM节点操作
  • 原文地址:https://www.cnblogs.com/mazhimazhi/p/14022630.html
Copyright © 2011-2022 走看看