在前一篇文章 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实现 蓝桥杯 算法提高 Monday-Saturday质因子

    试题 算法提高 Monday-Saturday质因子 资源限制 时间限制:1.0s 内存限制:256.0MB 问题描述 这个问题是个简单的与数论有关的题目,看起来似乎是"求正整数的所有质因子 ...

  2. java实现放麦子问题

    /* 你一定听说过这个故事.国王对发明国际象棋的大臣很佩服, 问他要什么报酬,大臣说:请在第 1 个棋盘格放 1 粒麦子, 在第 2 个棋盘格放 2 粒麦子,在第 3 个棋盘格放 4 粒麦子, 在第 ...

  3. svg 贝塞尔曲线图解(记录)

    path路径绘制中,绘制贝塞尔曲线的命令包括: Q 二次贝赛尔曲线 x1,y1 x,y T 平滑二次贝塞尔曲线 x,y C 曲线(curveto) x1,y1 x2,y2 x,y S 平滑曲线 x2, ...

  4. DirectX11 With Windows SDK--31 阴影映射

    前言 阴影既暗示着光源相对于观察者的位置关系,也从侧面传达了场景中各物体之间的相对位置.本章将起底最基础的阴影映射算法,而像复杂如级联阴影映射这样的技术,也是在阴影映射的基础上发展而来的. 学习目标: ...

  5. 本地配置gitee

    一 下载工具 Git-2.62.0-64-bit.exe 以上工具版本号不需要一样,安装完前两个后重新启动系统,再安装第3个. 二 码云网站注册 https://gitee.com/ 使用邮箱注册 注 ...

  6. JDK8在泛型类型推导上的变化

    概述 JDK8升级,大部分问题可能在编译期就碰到了,但是有些时候比较蛋疼,编译期没有出现问题,但是在运行期就出了问题,比如今天要说的这个话题,所以大家再升级的时候还是要多测测再上线,当然JDK8给我们 ...

  7. MATLAB实例:聚类网络连接图

    MATLAB实例:聚类网络连接图 作者:凯鲁嘎吉 - 博客园 http://www.cnblogs.com/kailugaji/ 本文给出一个简单实例,先生成2维高斯数据,得到数据之后,用模糊C均值( ...

  8. jQuery实现打飞机游戏

    玩法介绍:不同样式的飞机出来其它飞机会暂停飞行且处于无敌状态,子弹对它无效,你操纵的飞机不能碰到任何飞机,发出的子弹可以攻击正在飞行的飞机,每击落一架飞机会记录分数,你操纵的飞机碰到其它飞机即为游戏结 ...

  9. beta版 tomcat 应用监控指标

    指标是集合网络搜索得到的汇总并且现在在使用的,现在记录一下 数据平台:Prometheus v2.18.1 展示平台:Grafana 指标来源: 日志类的是mtail 其他都是通过jmx_export ...

  10. Codeforces Round #648 (Div. 2)

    链接 : https://codeforces.com/contest/1365/problems problem A 统计可用的行和列的最小值, 模2输出即可 /* * Author: RoccoS ...