在前一篇文章 HotSpot的二分模型中已经讲过,HotSpot采用了OOP-Klass模型描述Java的类和对象。Klass模型采用Klass类及相关子类来表示具体的Java类,可以理解这些类为Java类在C++ 中的对等体。一般 JVM 在加载 Class 文件时,会在方法区创建 Klass ,表示类的元数据,包括常量池、字段、方法等。

相关类的继承体系如下图所示。

Metadata是元数据类的基础类型,除了Klass会直接继承外,表示方法的Method与表示常量池的ConstantPool也会继承,这里只讨论Klass继承体系中涉及到的相关类。

整个Klass模型中涉及到的类主要提供了2个功能:

(1)提供C++层面的Java类(包括Java类和Java数组)表示

(2)方法分派

这一篇文章重点介绍一下Klass这个基础类型。

一个Klass对象(注意是Klass对象表示Java类的元数据,所以不同的Java类就用不同的Klass对象)代表一个Java类的元数据(相当于java.lang.Class对象)。所以Klass中要有描述Java类中常量池、字段、方法的能力,也就是能保存这些信息,同时还能提供一些方法供HotSpot的开发者操作这些信息。

Klass类及重要属性的定义如下:

class Klass : public Metadata {
...
protected:
// note: put frequently-used fields together at start of klass structure
// for better cache behavior (may not make much of a difference but sure won't hurt)
enum { _primary_super_limit = 8 }; // The "layout helper" is a combined descriptor of object layout.
// For klasses which are neither instance nor array, the value is zero.
//
// For instances, layout helper is a positive number, the instance size.
// This size is already passed through align_object_size and scaled to bytes.
// The low order bit is set if instances of this class cannot be
// allocated using the fastpath.
//
// For arrays, layout helper is a negative number, containing four
// distinct bytes, as follows:
// MSB:[tag, hsz, ebt, log2(esz)]:LSB
// where:
// tag is 0x80 if the elements are oops, 0xC0 if non-oops
// hsz is array header size in bytes (i.e., offset of first element)
// ebt is the BasicType of the elements
// esz is the element size in bytes
// This packed word is arranged so as to be quickly unpacked by the
// various fast paths that use the various subfields.
//
// The esz bits can be used directly by a SLL instruction, without masking.
//
// Note that the array-kind tag looks like 0x00 for instance klasses,
// since their length in bytes is always less than 24Mb.
//
// Final note: This comes first, immediately after C++ vtable,
// because it is frequently queried.
jint _layout_helper; // The fields _super_check_offset, _secondary_super_cache, _secondary_supers
// and _primary_supers all help make fast subtype checks. See big discussion
// in doc/server_compiler/checktype.txt
//
// Where to look to observe a supertype (it is &_secondary_super_cache for
// secondary supers, else is &_primary_supers[depth()].
juint _super_check_offset; // Class name. Instance classes: java/lang/String, etc. Array classes: [I,
// [Ljava/lang/String;, etc. Set to zero for all other kinds of classes.
Symbol* _name; // Cache of last observed secondary supertype
Klass* _secondary_super_cache;
// Array of all secondary supertypes
Array<Klass*>* _secondary_supers;
// Ordered list of all primary supertypes
Klass* _primary_supers[_primary_super_limit];
// java/lang/Class instance mirroring this class
oop _java_mirror;
// Superclass
Klass* _super;
// First subclass (NULL if none); _subklass->next_sibling() is next one
Klass* _subklass;
// Sibling link (or NULL); links all subklasses of a klass
Klass* _next_sibling; // All klasses loaded by a class loader are chained through these links
Klass* _next_link; // The VM's representation of the ClassLoader used to load this class.
// Provide access the corresponding instance java.lang.ClassLoader.
ClassLoaderData* _class_loader_data; AccessFlags _access_flags; // Access flags. The class/interface distinction is stored here. markOop _prototype_header; // Used when biased locking is both enabled and disabled for this type ...
}

下表对各个属性进行了简单的介绍。

字段名 作用
_layout_helper

对象布局的综合描述符。如果不是InstanceKlass或ArrayKlass,值为0。如果是InstantceKlass或

ArrayKlass时,这个值是个组合数字。

对InstanceKlass而言,组合数字中包含有表示对象的以字节为单位的内存占用大小,也就是说

InstanceKlass表示Java类,由这个Java类创建的对象所需要的大小。

对ArrayKlass而言,该值是一个组合数字,包含4部分,具体怎么组合和解析由子类实现:

  • tag:如果数组元素的类型为对象,值为0x80;否则值为0xC0,表示数组元素的类型为Java基本类型
  • sz::数组头元素的字节数
  • ebt:数组元素的类型,枚举值BasicType
  • esz:数组元素大小,以字节为单位
_name 类名,如java/lang/String,[Ljava/lang/String
_primary_supers

Klass指针数组,大小固定为8。_primary_supers代表了这个类的父类。例如IOException,是Exception的子类,

而Exception又是Throwable的子类。所以IOException的 _primary_supers是这样的:

[Throwable, Exception, IOException]。如果继承链过长,也就是当前类加上继承的类多于8个(默认值,可通过命令更改)时,

会将多出来的类存储到secondary_supers数组中。

 _super_check_offset 

快速查找supertype的一个偏移量,这个偏移量是相对于Klass对象起始地址的偏移量。如果当前类是IOException,

那么这个属性就指向_primary_supers数组中存储IOException的位置。当存储的类多于8个时,值与secondary_super_cache

相等。

_secondary_supers

Klass指针数组,一般存储类实现的接口,偶尔还会存储类

_secondary_super_cache

Klass指针,保存上一次查询父类的结果

_java_mirror oopDesc指针,此类对应的java/lang/Class对象,可以据此访问类的静态属性
_super Klass指针,父类 
_subklass Klass指针,子类
_next_sibling Klass指针,该类的下一个子类,也就是通过_subklass->next_sibling()来获取_subklass的兄弟子类
_next_link Klass指针,ClassLoader加载的下一个Klass
_class_loader_data ClassLoaderData指针,加载该类的ClassLoader
_access_flags 获取类的修饰符,如private、final、static、abstract 、native等
_prototype_header 在锁的实现过程中非常重要,后续在介绍锁时会介绍 

可以看到,能够通过Klass类中的相关属性保存Java类定义的一些信息,如_name保存Java类的名称、_super保存Java类实现的类型等。Klass类是Klass模型中定义的类的基类,所以只保存了Java类的一些必要信息,其它如常量池、方法、字段等会通过具体子类的属性来保存。

类的属性比较多,我们在后面解释类的过程中可以看到对相关属性的赋值操作。下面来看一个对属性赋值相对复杂一点的方法,如initialize_supers()方法,实现如下:

源代码位置: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;
} 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。  

下面举个例子,看一下是如何应用这几个属性的,例如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数组中的值并比较即可。  

另外还能调用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属性的值
}

方法的实现逻辑很简单,这里不过多介绍。  

还有对_layout_helper属性的设置相对复杂,在讲解InstanceKlass或ArrayKlass时介绍。  

相关文章的链接如下:

1、在Ubuntu 16.04上编译OpenJDK8的源代码

2、调试HotSpot源代码

3、HotSpot项目结构 

4、HotSpot二分模型 (1)

HotSpot的类模型(2)的更多相关文章

  1. HotSpot的类模型(3)

    上一篇 HotSpot的类模型(2) 介绍了类模型的基础类Klass的重要属性及方法,这一篇介绍一下InstanceKlass及InstanceKlass的子类. 2.InstanceKlass类 每 ...

  2. HotSpot的类模型(4)

    我们继续接着上一篇 HotSpot的类模型(3)分析,这次主要分析表示java数组的C++类. 4.ArrayKlass类 ArrayKlass继承自Klass,是所有数组类的抽象基类,类及重要属性的 ...

  3. HotSpot类模型之InstanceKlass

    上一篇 HotSpot源码分析之类模型 介绍了类模型的基础类Klass的重要属性及方法,这一篇介绍一下InstanceKlass及InstanceKlass的子类. 1.InstanceKlass类 ...

  4. HotSpot类模型之ArrayKlass

    上一篇分析了 HotSpot类模型之InstanceKlass ,这次主要分析表示java数组类型的C++类. 1.ArrayKlass类 ArrayKlass继承自Klass,是所有数组类的抽象基类 ...

  5. .NET使用DAO.NET实体类模型操作数据库

    一.新建项目 打开vs2017,新建一个项目,命名为orm1 二.新建数据库 打开 SqlServer数据库,新建数据库 orm1,并新建表 student . 三.新建 ADO.NET 实体数据模型 ...

  6. laravel5.8笔记五:基类控制器和基类模型

    建立基类的目的就是为了方便继承.比如:Admin模块访问,是否登陆.检测登陆可以写到基类里面 控制器基类 原始基类:app\Http\Controllers\Controller.php,我们下面要做 ...

  7. Paddle Graph Learning (PGL)图学习之图游走类模型[系列四]

    Paddle Graph Learning (PGL)图学习之图游走类模型[系列四] 更多详情参考:Paddle Graph Learning 图学习之图游走类模型[系列四] https://aist ...

  8. DL4NLP——词表示模型(一)表示学习;syntagmatic与paradigmatic两类模型;基于矩阵的LSA和GloVe

    本文简述了以下内容: 什么是词表示,什么是表示学习,什么是分布式表示 one-hot representation与distributed representation(分布式表示) 基于distri ...

  9. 自定义MVC框架之工具类-模型类

    截止目前已经改造了5个类: ubuntu:通过封装验证码类库一步步安装php的gd扩展 自定义MVC框架之工具类-分页类的封装 自定义MVC框架之工具类-文件上传类 自定义MVC框架之工具类-图像处理 ...

随机推荐

  1. java实现还款计算

    标题: 还款计算 银行贷款的等额本息还款方法是: 每月还固定的金额,在约定的期数内正好还完(最后一个月可能会有微小的零头出入). 比如说小明在银行贷款1万元.贷款年化利率为5%,贷款期限为24个月. ...

  2. Python数据分析之双色球高频数据统计

    Step1:基础数据准备(通过爬虫获取到),以下是从第一期03年双色球开奖号到今天的所有数据整理,截止目前一共2549期,balls.txt 文件内容如下 : 备注:想要现成数据的可以给我发邮件哟~ ...

  3. 大顶堆与小顶堆应用---寻找前k小数

    vector<int> getLeastNumber(vector<int>& arr,int k){ vector<int> vec(k,); if(== ...

  4. Flutter upgrade更新版本引发的无法启动调试APP的错误 target:kernel_snapshot failed”

    前言 我的主机上的Flutter 本地的分支是在 beta,因为去年想尝鲜Flutter Web,所以一直没切回来stable分支. 早上打开VSCode,右下角弹出了Flutter upgrade的 ...

  5. Jquery封装: 地区选择联动插件

    请点击下载百度云链接: 链接: https://pan.baidu.com/s/1plVmdJT2O4fLJokyJDQA2g 密码: aqt2

  6. scws中文分词安装和使用

    一.下载源码 wget http://www.xunsearch.com/scws/down/scws-1.2.3.tar.bz2 tar xvjf scws-1.2.3.tar.bz2 二.执行配置 ...

  7. git环境配置 | GitHub

    注册完GitHub之后,需要配置git,其主要的目的是为了方便文件的上传.下载等. 一. git下载 https://git-scm.com/downloads 在git官网找到相应版本的git下载安 ...

  8. Python流程控制语句详解

    1.程序结构 计算机在解决问题时,分别是顺序执行所有语句.选择执行部分语句.循环执行部分语句,分别是:顺序结构.选择结构.循环结构.如下图: 2.选择语句 2.1最简单的if语句 Python使用保留 ...

  9. python数据预处理

    缺失值处理 import pandas as pda import numpy as npy import matplotlib.pylab as pyl # data=pda.read_excel( ...

  10. mysql explain的type的

    导语 很多情况下,有很多人用各种select语句查询到了他们想要的数据后,往往便以为工作圆满结束了.这些事情往往发生在一些学生亦或刚入职场但之前又没有很好数据库基础的小白身上,但所谓闻道有先后,只要我 ...