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

  • allocated(已分配内存)
  • loaded(从class文件读取加载到内存中)
  • linked(已经成功链接和校验)
  • being_initialized(正在初始化)
  • fully_initialized(已经完成初始化)
  • initialization_error(初始化异常)
_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)

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

  1. HotSpot的类模型(4)

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

  2. HotSpot的类模型(2)

    在前一篇文章 HotSpot的二分模型中已经讲过,HotSpot采用了OOP-Klass模型描述Java的类和对象.Klass模型采用Klass类及相关子类来表示具体的Java类,可以理解这些类为Ja ...

  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. TZOJ 公交车

    描述 公交车在一条笔直的道路(道路宽度忽略,设为X轴)上行驶,按顺序路上有若干个站点(X坐标值),crq的家也在道路旁某个位置h(X坐标值),现在crq想知道哪个站点下车离家最近. 输入 输入数据的第 ...

  2. js数据劫持 Object.defineProperty() 作用

    原生js Object.defineProperty() 作用 假设我们有一个obj对象,我们要给他设置一个name属性会这么做 Object.defineProperty()也可以设置对象属性 这个 ...

  3. css背景图片加载失败,页面部分图标无法显示

    1.问题表现:首屏缺失部分图标.点击按钮切换为激活状态时,部分按钮的激活态图标无法显示. 2.问题原因:网络极差,断断续续,点击时添加class:active变为激活态, active.png这张图片 ...

  4. java解决poi导出excel文字水印,导出excel不可操作问题

    首先需求是用户提出导出excel数据需使用水印备注其用途: 其实就是在导出excel的同时带有自定义文字水印的导出. 那么我们首先想到的肯定是以一个什么样的思路去解决该问题,首先查找poi导出exce ...

  5. 第一章03-Activity的启动模式

    Activity的LaunchMode Android中提供了四中Activity的启动模式 1. standard 2. singleTop 3. singleTask 4. signleInsta ...

  6. 内核与驱动文件的version magic匹配问题

    https://blog.csdn.net/yubing_615/article/details/52183185 1.问题:本地编译的一整套底层代码down到设备跑都正常,但是由这套代码上传SVN服 ...

  7. 最全面的SourceTree账号注册教程

    前言: 作为一个国内开发者而言使用Git操作神器SoureTree最大的问题就是账号注册问题,因为注册账号的链接在不翻墙的情况下基本上是打不开的(弄过的童鞋应该都体会过),所以有的时候我们需要借助一些 ...

  8. 全网最完整的Redis入门指导

    前言 本文提供全网最完整的Redis入门指导教程,下面我们从下载Redis安装包开始,一步一步的学习使用. 下载Redis 官网提供的Redis安装包是服务于Linux的,而我们需要在Window下使 ...

  9. (八)easyexcel的使用

    使用手册:https://www.yuque.com/easyexcel/doc/easyexcel 主要注意的点就是修改监听器为通用的监听器 原监听器: package read; import j ...

  10. python基础003----标准数据类型

    一.标准数据类型 在python中,只要定义了一个变量,而且它有数据,那么它的类型就已经确定了,不需要开发者主动去说明它的类型,系统会自动识别.可以用type(变量名)来查看变量的类型.常见的变量类型 ...