在线时间:8:00-16:00
迪恩网络APP
随时随地掌握行业动态
扫描二维码
关注迪恩网络微信公众号
HotSpot采用了OOP-Klass模型描述Java的类和对象。Klass模型采用Klass类及相关子类的对象来描述具体的Java类。一般HotSpot JVM 在加载Java的Class 文件时,会在方法区创建 Klass ,用来保存Java类的元数据,包括常量池、字段、方法等。 Klass模型中相关类的继承体系如下图所示。
Metadata是元数据类的基础类型,除了Klass会直接继承外,表示方法的Method与表示常量池的ConstantPool也会继承,这里只讨论Klass继承体系中涉及到的相关类。 整个Klass模型中涉及到的C++类主要提供了2个功能: (1)提供C++层面的Java类型(包括Java类和Java数组)表示,也就是用C++类的对象来描述Java类型。 (2)方法分派 这一篇文章重点介绍一下Klass这个基础类型。 一个Klass对象(注意是Klass对象表示Java类的元数据,所以不同的Java类就用不同的Klass对象表示)代表一个Java类的元数据(相当于 Klass类及重要属性的定义如下: 源代码位置:hotspot/src/share/vm/oops/klass.hpp 下表对各个属性进行了简单的介绍。
可以看到,能够通过Klass类中的相关属性保存Java类定义的一些信息,如_name保存Java类的名称、_super保存Java类实现的类型等。Klass类是Klass模型中定义的C++类的基类,所以此类对象只保存了Java类的一些必要信息,其它如常量池、方法、字段等会通过Klass类的具体子类的相关属性来保存。 类的属性比较多,我们在后面解释类的过程中可以看到对相关属性的赋值操作。 1、_layout_helper _layout_helper是一个组合属性。如果当前的类表示一个Java数组类型时,这个属性的值比较复杂。通常会调用如下函数生成值: jint Klass::array_layout_helper(BasicType etype) { assert(etype >= T_BOOLEAN && etype <= T_OBJECT, "valid etype"); bool isobj = (etype == T_OBJECT); int tag = isobj ? _lh_array_tag_obj_value : _lh_array_tag_type_value; // Note that T_ARRAY is not allowed here. // 在64位系统下,存放_metadata的空间大小是8字节,_mark是8字节, // length是4字节,对象头为20字节,由于要按8字节对齐,所以会填充4字节,最终占用24字节 int hsize = arrayOopDesc::base_offset_in_bytes(etype); // hsize表示数组头部大小 int esize = type2aelembytes(etype); // Java基本类型元素需要占用的字节数 int esz = exact_log2(esize); // 例如Java基本类型元素占用4个字节,则存储的是2 int lh = array_layout_helper(tag, hsize, etype, esz); return lh; } 其中用到2个枚举常量,如下: _lh_array_tag_type_value = ~0x00, _lh_array_tag_obj_value = ~0x01 _lh_array_tag_type_value的二进制表示为32个1:11111111111111111111111111111111,其实也就是0xC0000000 >> 30,做算术右移,负数的最高位补1。 _lh_array_tag_obj_value的二进制表示为最高位31个1:11111111111111111111111111111110,其实也就是0x80000000 >> 30,做自述右移,负数的最高位补1。 调用的arrayOopDesc::base_offset_in_bytes()函数及调用的相关函数的实现如下: // Returns the offset of the first element. static int base_offset_in_bytes(BasicType type) { return header_size(type) * HeapWordSize; } // Should only be called with constants as argument (will not constant fold otherwise) // Returns the header size in words aligned to the requirements of the array object type. static int header_size(BasicType type) { size_t typesize_in_bytes = header_size_in_bytes(); if( Universe::element_type_should_be_aligned(type) ){ return (int)align_object_offset( typesize_in_bytes/HeapWordSize ); }else { return (int)typesize_in_bytes/HeapWordSize; } } // 在64位系统下,存放_metadata的空间大小是8字节,_mark是8字节,length是4字节,对象头为20字节, // 由于要按8字节对齐,所以会填充4字节,最终占用24字节 static int header_size_in_bytes() { intptr_t temp = length_offset_in_bytes() + sizeof(int); // sizeof(int) size_t hs = align_size_up(temp,HeapWordSize); return (int)hs; } // The _length field is not declared in C++. It is allocated after the // declared nonstatic fields in arrayOopDesc if not compressed, otherwise // it occupies the second half 下半场 of the _klass field in oopDesc. static int length_offset_in_bytes() { if(UseCompressedClassPointers){ return klass_gap_offset_in_bytes(); }else{ sizeof(arrayOopDesc); } } 代码的逻辑非常清晰,这里不过多介绍。最终会在Klass::array_layout_helper()函数中调用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位 } 对_lh_array_tag_type_value与_lh_array_tag_obj_value值左移30位后,第32位上肯定为1,所以最终计算出的值是一个小于0的数。而非数组类型,一般由InstanceKlass对象表示的Java类来说,计算的属性值如下: static jint instance_layout_helper(jint size, bool slow_path_flag) { if(slow_path_flag){ return (size << LogHeapWordSize) | _lh_instance_slow_path_bit; }else{ return (size << LogHeapWordSize) | 0; // LogHeapWordSize=3 } } size为对象的、以字节为单位的内存占用大小,所以肯定是一个正数。这样就可以通过_layout_helper来判断类型了。 2、_primary_supers、_super_check_offset、_secondary_supers与_secondary_super_cache 这几个属性完全是为了加快判定父子关系等逻辑而加入的。下面看initialize_supers()函数中是如何初始化这几个属性的。函数的第1部分实现如下: 源代码位置:hotspot/src/share/vm/oops/klass.cpp void Klass::initialize_supers(Klass* k, TRAPS) { // // 当前类的父类k可能为NULL,例如Object的父类为NULL if (k == NULL) { set_super(NULL); _primary_supers[0] = this; } // k就是当前类的直接父类,如果有父类,那么super()一般为NULL,如果k为NULL,那么就是Object类,从下面的断言也可以看出 else if (k != super() || k == SystemDictionary::Object_klass()) { set_super(k); // 设置Klass的_super属性 Klass* sup = k; int sup_depth = sup->super_depth(); juint my_depth = MIN2(sup_depth + 1, (int)primary_super_limit()); // primary_super_limit()方法得到的值一般默认为8 // 当父类的的继承链长度大于等于primary_super_limit()时,当前的深度只能是primary_super_limit(),也就是8,因为_primary_supers中只存储8个类 if (!can_be_primary_super_slow()){ my_depth = primary_super_limit(); // 8 } for (juint i = 0; i < my_depth; i++) { // my_depth默认的值为8 _primary_supers[i] = sup->_primary_supers[i]; } Klass* *super_check_cell; if (my_depth < primary_super_limit()) { // primary_super_limit()的默认为8 _primary_supers[my_depth] = this; super_check_cell = &_primary_supers[my_depth]; } else { // Overflow of the primary_supers array forces me to be secondary. super_check_cell = &_secondary_super_cache; } // 通过_super_check_offset这个偏移量可以快速定义到当前在_primary_supers中的位置 juint _super_check_offset = (address)super_check_cell - (address) this; set_super_check_offset( _super_check_offset ); // 设置Klass中的_super_check_offset属性 } // 第2部分代码在下面 } 在设置当前类的父类时通常都会调用initialize_supers方法,同时也会设置_primary_supers、super_check_offset,如果继承链过长,还有可能设置secondary_supers、secondary_super_cache等值。这此属性中存储继承链中涉及到的类以方便快速的进行类关系之间的判断,例如父子关系的判断。 方法的第2部分代码实现如下: if (secondary_supers() == NULL) { KlassHandle this_kh (THREAD, this); // Now compute the list of secondary supertypes. // Secondaries can occasionally be on the super chain, // if the inline "_primary_supers" array overflows. int extras = 0; Klass* p; for (p = super(); // 当p不为NULL并且p已经存储在了_secondary_supers数组中时,条件为true // 也就是当前类的父类多于8个,将多出来的存储到了_secondary_supers数组中了 !(p == NULL || p->can_be_primary_super()); p = p->super()) { ++extras; } // 计算secondaries需要的大小,因为secondaries数组中还需要存储当前类的所有实现接口(包括直接和间接实现的接口) // Compute the "real" non-extra secondaries. GrowableArray<Klass*>* secondaries = compute_secondary_supers(extras); if (secondaries == NULL) { // extras为0时直接返回,不需要额外的处理 // secondary_supers set by compute_secondary_supers return; } GrowableArray<Klass*>* primaries = new GrowableArray<Klass*>(extras); for ( p = this_kh->super(); !(p == NULL || p->can_be_primary_super()); p = p->super() ){ primaries->push(p); } // Combine the two arrays into a metadata object to pack the array. // The primaries are added in the reverse order, then the secondaries. int new_length = primaries->length() + secondaries->length(); Array<Klass*>* s2 = MetadataFactory::new_array<Klass*>(class_loader_data(), new_length, CHECK); int fill_p = primaries->length(); for (int j = 0; j < fill_p; j++) { s2->at_put(j, primaries->pop()); // add primaries in reverse order.也就是父类永远在数组前,子类永远在数组后 } for( int j = 0; j < secondaries->length(); j++ ) { s2->at_put(j+fill_p, secondaries->at(j)); // add secondaries on the end. } this_kh->set_secondary_supers(s2); // 设置_secondary_supers属性 } 可以看到,会将父亲继承链中多于8个的父类存储到secondary_supers数组中,不过因为继承链一般都不会多于8个,所以设置了默认值为8。 下面举个例子,看看这几个属性是如何存储值的,如下: interface IA{} interface IB{} class A{} class B extends A{} class C extends B{} class D extends C{} public class Test extends D implements IA,IB {} 配置-XX:FastSuperclassLimit=3后,_primary_supers数组中就最多只能存储3个类了。值如下: _primary_supers[Object,A,B] _secondary_supers[C,D,Test,IA,IB] 由于当前类Test的继承链过长,导致C、D和Test只能存储到_secondary_supers。所以此时_super_check_offset会指向C,也就是_secondary_supers中存储的第1个元素。 下面举个例子,看一下这几个属性如何应用。例如is_subtype_of()方法,实现如下: // subtype check: true if is_subclass_of, or if k is interface and receiver implements it bool is_subtype_of(Klass* k) const { // 判断当前类是否为k的子类 juint off = k->super_check_offset(); Klass* sup = *(Klass**)( (address)this + off ); const juint secondary_offset = in_bytes(secondary_super_cache_offset()); if (sup == k) { return true; } else if (off != secondary_offset) { return false; } else { return search_secondary_supers(k); } } 当通过_super_check_offset获取到的类与k相同时,那么k存在于当前类的继承链上,肯定有父子关系。 如果k存在于_primary_supers数组中,那么通过_super_check_offset就可快速判断,如果k存在于_secondary_supers中,那么需要调用search_secondary_supers()来判断。 调用的search_secondary_supers()方法的实现如下: bool Klass::search_secondary_supers(Klass* k) const { // Put some extra logic here out-of-line, before the search proper. // This cuts down the size of the inline method. // This is necessary, since I am never in my own secondary_super list. if (this == k){ return true; } // Scan the array-of-objects for a match int cnt = secondary_supers()->length(); for (int i = 0; i < cnt; i++) { if (secondary_supers()->at(i) == k) { ((Klass*)this)->set_secondary_super_cache(k); // 设置_secondary_super_cache属性,保存这次查询的结果 return true; } } return false; } 可以看到,属性_secondary_super_cache保存了这一次父类查询的结果。查询的逻辑很简单,遍历_secondary_supers数组中的值并比较即可。 3、_super、_subklass、_next_sibling 由于Java类是单继承,所以可通过_super、_subklass、_next_sibling属性可直接找到当前类的父类或所有子类。调用Klass::append_to_sibling_list()函数设置_next_sibling与_subklass的值,方法的实现如下: void Klass::append_to_sibling_list() { // add ourselves to superklass' subklass list InstanceKlass* super = superklass(); // 获取到_super属性的值 if (super == NULL) return; // special case: class Object Klass* prev_first_subklass = super->subklass_oop(); // 获取_subklass属性的值 if (prev_first_subklass != NULL) { // set our sibling to be the superklass' previous first subklass set_next_sibling(prev_first_subklass); // 设置_next_sibling属性的值 } // make ourselves the superklass' first subklass super->set_subklass(this); // 设置_subklass属性的值 } 方法的实现逻辑很简单,这里不过多介绍。 其它文章: 1、在Ubuntu 16.04上编译OpenJDK8的源代码(配视频) 搭建过程中如果有问题可直接评论留言或加作者微信mazhimazh。 作者持续维护的个人博客 classloading.com。 B站上有HotSpot源码分析相关视频 https://space.bilibili.com/27533329 关注公众号,有HotSpot源码剖析系列文章!
|
2023-10-27
2022-08-15
2022-08-17
2022-09-23
2022-08-13
请发表评论