一、Klass-OOP概述
1.1 基本概念
在HotSpot中,
Klass
来描述 Java 类,不同的Java类由不同的Klass
实例对象定义;oop(ordinary object pointer, 普通对象指针)
定义为一块内存区域;OopDesc
来描述这块内存的内容,即描述Java实例对象,不同的Java实例对象对应不同的OopDesc
对象。
JVM 使用 C++ 编写的,C++ 定义了2个类 Klass
、OopDesc
分别用来描述Java中类和对象, 所以Java中的类和对象对于C++而言,均是 C++ 创建的对象。
类比注解和元注解。C++定义的Java类产生的对象就是元注解;Java代码产生的对象就是注解。

1、 JAVA代码通过
new Object()
创建对象,图示{1};2、JVM会加载对应的
class
,解析生成Klass
对象,图示{2}、{3};3、JVM更加加载的
Klass
对象,创建OopDesc
对象,图示{4}。
注意:
- Java的每个类,在JVM中都有一个对应的Klass类实例与之对应,存储类的元信息如:常量池、属性信息、方法信息......
- Klass对象实例由JVM自己产生,放在JVM元空间;
- OopDesc对象实例由Java代码定义产生,放在JVM堆内存;
- 简言之,JVM创建的对象统一放在JVM元空间,java代码创建的对象统一放在堆内存;
1.2 具体实现
为了更加精细化管理Java中的类和对象,JVM层面也定义很多了Klass
的子类和OopDesc
的子类。
大的维度为普通类 (Instance)和数组类(Array):
InstanceKlass
、InstanceOopDesc
分别用于普通的Java 类、普通的Java对象;ArrayKlass
、ArrayOopDesc
分别用于数组类型的Java 类、数组类型的Java对象;
对于普通的Java 类InstanceKlass
而言,分别为Java中3个特殊的类定义了子类:
InstanceMirrorKlass
java.lang.ClassInstanceRefKlass
java.lang.ref.ReferenceInstanceClassLoaderKlass
java.lang.ClassLoader

二、代码简析
2.1、InstanceKlass代码简析
InstanceKlass 里面的变量基本和字节码(更多详情参见字节码(二))中的内容一致,本质就是把字节码加载并解析到JVM中。
cpp
class InstanceKlass: public Klass {
//......
protected:
// 保存该类使用的所有注解
Annotations* _annotations;
// 数组元素为该类的数组Klass指针,即数组内部元素的 Klass 指向数组的 klass
Klass* _array_klasses;
// 常量池信息
ConstantPool* _constants;
// java 内部类信息
Array<jushort>* _inner_classes;
// 举例:java中Object类在字节码中的名字: "[Ljava/lang/Object;"
Symbol* _array_name;
// java 类实例成员占用内存大小,以字(word)为单位;在为当前类表示的Java类所创建的对象(使用oop表示)分配内存时使用
int _nonstatic_field_size;
// java 类成员占用内存大小,以字(word)为单位;在为当前类表示的Java类所创建的java.lang.Class对象(使用oop表示)分配内存时使用
int _static_field_size;
// 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;
// java 字段总数量
u2 _java_fields_count;
// 非静态的oop map block需要占用的内存大小,以字为单位,GC时快速标记对象可达,快速构造引用链
int _nonstatic_oop_map_size;
OopMapBlock* _nonstatic_oop_map
u2 _minor_version; // minor version number of class file
u2 _major_version; // major version number of class file
// 执行此类初始化的Thread指针
Thread* _init_thread; // Pointer to current thread doing initialization (to handle recusive initialization)
// Java虚函数表(vtable)所占用的内存大小,以字为单位
int _vtable_len; // length of Java vtable (in words)
// Java接口函数表(itable)所占用的内存大小,以字为单位
int _itable_len; // length of Java itable (in words)
// 用于存放java类所有方法
OopMapCache* volatile _oop_map_cache; // OopMapCache for all methods in the klass (allocated lazily)
// JNI方法操作属性
JNIid* _jni_ids; // First JNI identifier for static fields in this class
// JNI方法操作方法
jmethodID* _methods_jmethod_ids; // jmethodIDs corresponding to method_idnum, or NULL if none
// 依赖的本地方法,以根据其_next属性获取下一个nmethod
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)
// allocated(已分配内存)
// loaded(从class文件读取加载到内存中)
// linked(已经成功链接和校验)
// being_initialized(正在初始化)
// fully_initialized(已经完成初始化)
// initialization_error(初始化异常)
// Place the _init_state here to utilize the unused 2-byte after
// _idnum_allocated_count.
u1 _init_state; // state of class
// 引用类型: 强引用、软、弱、虚 、FinalReference
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; // 类的字段属性,每个字段的6个属性 如:访问控制、属性名、初始值,内存中的偏移量;此外,保存完所有属性之后还可能会保存泛型签名信息。
// _java_mirror ,指向java层面的class对象(类.class)
oop _java_mirror
...
// 解析一个类时计算内存大小
int size(int vtable_len,
int itable_len,
int nonstatic_oop_map_size,
bool isinterf,
bool is_anonymous)
{
return align_object_size(header_size() + // InstanceKlass类本身占用的内存大小
align_object_offset(vtable_length) +
align_object_offset(itable_length) +
// [EMBEDDED nonstatic oop-map blocks] size in words = nonstatic_oop_map_size
(
(is_interface || is_anonymous) ?
align_object_offset(nonstatic_oop_map_size) :
nonstatic_oop_map_size
) +
// [EMBEDDED implementor of the interface] only exist for interface
(
is_interface ? (int)sizeof(Klass*)/HeapWordSize : 0
) +
// [EMBEDDED host klass ] only exist for an anonymous class (JSR 292 enabled)
(
is_anonymous ? (int)sizeof(Klass*)/HeapWordSize : 0)
);
}
// Sizing (in words) 计算 InstanceKlass类所需内存
static int header_size(){
return align_object_offset(sizeof(InstanceKlass)/HeapWordSize); // 以HeapWordSize为单位,64位一个字为8字节,所以值为8
}
}
2.2、InstanceRefKlass代码简析
java.lang.ref.Reference
的子类需要使用InstanceRefKlass 类来表示,因为这些类需要垃圾回收器特殊处理,具体详见细说Java 引用(强、软、弱、虚)和 GC 流程(一)
cpp
class InstanceRefKlass : public InstanceKlass{
// reference type, 引用类型: 软、弱、虚 、FinalReference ,该字段继承自InstanceKlass
//u1 _reference_type;
}
enum ReferenceType {
REF_NONE, // Regular class 强引用
// 比如:用户自定义的 Reference 子类
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
};
2.3、InstanceMirrorKlass代码简析
cpp
class InstanceMirrorKlass: public InstanceKlass{
// 增加了一个静态属性_offset_of_static_fields,用来描述静态字段的起始偏移量
static int _offset_of_static_fields;
}
InstanceMirrorKlass
对象用于表示 java.lang.Class
类,那java.lang.Class
特殊在哪里,需要定制InstanceKlass?
- JAVA 类中变量有类变量(static)和实例变量,实例变量存在对象实例(
oop
)中,类变量存储在 java.lang.Class对象中,如下图所示:
重点来了,Class自身也是个java类,Class自己也有类变量,那Class自己的类变量放哪里? 这很像【鸡生蛋,蛋生鸡】的问题,得有个终止条件啊?Java层面解决不了了,只能借助 C++ 之前定义的 InstanceKlass 了来存放了类变量了
那Class的实例变量存哪里呢?貌似也没有Class实例,也只能放在InstanceKlass 了;这样一来,Class的类变量和实例变量均在InstanceKlass 里了,为了好区分,需要新增字段_offset_of_static_fields
了,InstanceMirrorKlass
也就应运而生了。
2.3.1 InstanceMirrorKlass实例对象放哪里?

文章一开始,我们将Klass对象放在了JVM元空间,Java层面创建的对象放在JVM堆内存,而普通类的class对象Java层面的,同样会放在JVM堆内存;
对于 java.lang.Class
的Klass对象即InstanceMirrorKlass实例对象,我们需要特殊对待,把它放在JVM堆内存。
小节:JAVA中,类的class对象和静态字段都存放在JVM堆内存中。
2.4、 InstanceClassLoaderKlass代码解析
cpp
class InstanceClassLoaderKlass: public InstanceKlass{
// 增加了新的oop遍历方法,主要用于类加载器依赖遍历使用。
}
2.5、OopDesc 代码简析
cpp
class OopDesc {
private:
volatile markoop_mark;// 标记字
union _metadata {
Klass* _klass; // klass 指针;
narrowKlass _compressed_klass; // klass 压缩指针;
} _metadata;
}
这代码熟悉吗,这不是JAVA对象的头部吗,markWord 和 类型指针(java 指针细节详情参见Java 引用是4个字节还是8个字节?)
三、Klass-OOP 内存分布
经过前面学习,相信下图你已经可以完全理解了。

类加载过程
1、通过类的全限定名(包名+类名)获取存储该类的class文件;
2、解析成运行时数据,即
instanceKlass
实例,存放在元空间;3、在堆区生成该类的Class对象,即
instanceMirrorKlass
实例;
可以看到,通过Object.class
我们需要经过 {1}、{2} 才能得到结果。