上一篇 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* _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 * _methods; // Default Method Array, concrete methods inherited from interfaces Array * _default_methods; // Interface (Klass*s) this class declares locally to implement. Array * _local_interfaces; // Interface (Klass*s) this class implements transitively. Array * _transitive_interfaces; // Int array containing the vtable_indices for default_methods // offset matches _default_methods offset Array * _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 * _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,定义了如下常量值:
|
_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)