咱们继续接着上一篇 HotSpot的类模型(3)分析,此次主要分析表示java数组的C++类。html
ArrayKlass继承自Klass,是全部数组类的抽象基类,类及重要属性的定义以下: java
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的基础上增长的属性以下表所示。bootstrap
字段 | 做用 |
_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个虚方法可被用来继承和重写,以下:数组
_dimension、_higher_dimension与_lower_dimension对于一维及多维数组的描述很是重要,属性值的设置相对简单,这里不在介绍。函数
(1)TypeArrayKlass类oop
TypeArrayKlass是ArrayKlass的子类,用于表示数组元素是基本类型的数组this
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文件中的全局变量,以下:component
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个属性,以下:
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中其它的属性设置也并不复杂,这里不在介绍。
相关文章的连接以下:
一、在Ubuntu 16.04上编译OpenJDK8的源代码
关注公众号,有HotSpot源码剖析系列文章!