HotSpot的类模型(4)

我们继续接着上一篇 HotSpot的类模型(3)分析,这次主要分析表示java数组的C++类。

4、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的基础上增加的属性如下表所示。

字段作用
_dimensionint类型,表示数组的维度,记为n
_higher_dimensionKlass指针,表示对n+1维数组Klass的引用
_lower_dimensionKlass指针,表示对n-1维数组Klass的引用
_vtable_lenint类型, 虚函数表的长度
_component_mirroroop, 数组元素对应的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对于一维及多维数组的描述非常重要,属性值的设置相对简单,这里不在介绍。

5、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) {
  assert(TypeArrayKlass::header_size() <= InstanceKlass::header_size(),
      "array klasses must be same size as InstanceKlass");

  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的构造函数如下:

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为基本类型,所以值为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对象的InstanceMirrorKlass对象产生关系,相关属性最终的值如下所示。

TypeArrayKlass._component_mirror=InstanceMirrorKlass

InstanceMirrorKlass._array_klass=TypeArrayKlass

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

(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类)进行创建。

最终表示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二分模型 (1)

6、HotSpot的类模型(2)  

7、HotSpot的类模型(3) 

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

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

鸠摩(马智)

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

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

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

打赏作者

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

抵扣说明:

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

余额充值