前面可以看出类加载缓存是封装成 Klass 保存在 SystemDictionary::_dictionary 中的,
OOP-Klass 模型概述:
HotSpot JVM 并没有根据 Java 实例对象直接通过虚拟机映射到新建的 C++ 对象,而是设计了一个 oop-klass 模型。
oop 指的是 Ordinary Object Pointer(普通对象指针,指向被创建的对象),它用来表示对象的实例信息,看起来像个指针实际上是藏在指针里的对象;而 klass 则包含元数据和方法信息,用来描述 Java 类。
为何要设计这样一个一分为二的对象模型呢?这是因为 HotSopt JVM 的设计者不想让每个对象中都含有一个 vtable(虚函数表),所以就把对象模型拆成 klass 和 oop。
其中 oop 中不含有任何虚函数,而 klass 就含有虚函数表,可以进行 method dispatch。
这个模型其实是参照的 Strongtalk VM 底层的对象模型。
虚拟机将字节码文件以流的形式加载到内存中,再将字节码文件各信息组合成虚拟机需要的对象模型和内存模型存储到MateSpace中。最后初始化对象模型,以备使用,依照对象模型,虚拟机可以按指令在堆中创建无数有限个对象。
Oops模块在 hotspot/src/share/vm/oops 下:
1.Klass
JVM在运行时,需要一种用来标识Java内部类型的机制。HotSpot的解决方案是,为每一个已加载的Java类创建一个instanceKlass对象,用来在JVM层表示Java类。
一个 Klass 对象代表一个类的元数据。类加载之后就会在云数据区域创建这个类的Klass,通过它可以获取这个类的常量池、字段、方法等信息。
klass体系如下:
class Klass;//klassOop的一部分,用来描述语言层的类型
class InstanceKlass;//在虚拟机层面描述一个Java类
class InstanceMirrorKlass;;//专有instantKlass,表示java.lang.Class的Klass
class InstanceClassLoaderKlass;
class InstanceRefKlass;
class ArrayKlass;//表示array类型的抽象基类
class ObjArrayKlass;
class TypeArrayKlass;
类数据这样的一个C++结构体实例也是一个对象,这个对象存在在方法区里。但是这个类数据对象即 InstanceKlass 是给VM内部用的,并不直接暴露给 Java 层。
在 HotSpot VM 里,java.lang.Class 的实例被称为“Java mirror”,意思是它是 VM 内部用的 klass 对象的“镜像”;
作用是把 InstanceKlass 对象包装了一层来暴露给 Java 层(开发者)使用,但主要用途是提供反射访问;
创建时机是加载->连接->初始化 的初始化阶段。
每个 Java 对象的对象头里,_klass 字段会指向一个VM内部用来记录类的元数据用的 InstanceKlass 对象
insanceKlass里有个 _java_mirror 字段(见下图),指向该类所对应的Java镜像——java.lang.Class实例
另外,HotSpot VM会给 Class 对象注入一个隐藏字段“klass”,用于指回到其对应的 InstanceKlass 对象。这样,klass与mirror之间就有双向引用:
类加载器可以装载类,这些类被HotSpot VM装载后,都以InstanceKlass实例表示(其实还可能是更具体的InstanceRefKlass、InstanceMirrorKlass或InstanceClassLoaderKlass实例)
Klass体系中 InstanceKlass类,每个加载的类都会生成 InstanceKlass 对象。
结构如下,代码见 instanceKlass.cpp :
class InstanceKlass: public Klass {
friend class VMStructs;
friend class ClassFileParser;
friend class CompileReplay;
enum ClassState {
allocated, // allocated (but not yet linked)
loaded, // loaded and inserted in class hierarchy (but not linked yet)
linked, // successfully linked/verified (but not initialized yet)
being_initialized, // currently running class initializer
fully_initialized, // initialized (successfull final state)
initialization_error // error happened during initialization
};
static int number_of_instance_classes() { return _total_instanceKlass_count; }
private:
static volatile int _total_instanceKlass_count;
protected:
Annotations* _annotations;
Klass* _array_klasses;
ConstantPool* _constants;
Array<jushort>* _inner_classes;
char* _source_debug_extension;
Symbol* _array_name;
int _nonstatic_field_size;
int _static_field_size; // number words used by static fields (oop and non-oop) in this klass
u2 _generic_signature_index;
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
bool _is_marked_dependent; // used for marking during flushing and deoptimization
bool _has_unloaded_dependent;
enum {
_misc_rewritten = 1 << 0, // methods rewritten.
_misc_has_nonstatic_fields = 1 << 1, // for sizing with UseCompressedOops
_misc_should_verify_class = 1 << 2, // allow caching of preverification
_misc_is_anonymous = 1 << 3, // has embedded _host_klass field
_misc_is_contended = 1 << 4, // marked with contended annotation
_misc_has_default_methods = 1 << 5, // class/superclass/implemented interfaces has default methods
_misc_declares_default_methods = 1 << 6 // directly declares default methods (any access)
};
u2 _misc_flags;
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)
MemberNameTable* _member_names; // Member names
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
BreakpointInfo* _breakpoints; // bpt lists, managed by Method*
GrowableArray<PreviousVersionNode *>* _previous_versions;
JvmtiCachedClassFileData* _cached_class_file;
volatile u2 _idnum_allocated_count; // JNI/JVMTI: increments with the addition of methods, old ids don't change
u1 _init_state; // state of class
u1 _reference_type; // reference type
JvmtiCachedClassFieldMap* _jvmti_cached_class_field_map; // JVMTI: used during heap iteration
NOT_PRODUCT(int _verify_count;) // to avoid redundant verifies
Array<Method*>* _methods;
Array<Method*>* _default_methods;
Array<Klass*>* _local_interfaces;
Array<Klass*>* _transitive_interfaces;
Array<int>* _method_ordering;
Array<int>* _default_vtable_indices;
Array<u2>* _fields;
}
下面将通过 HSDB 工具查看目类的 InstanceKlass,
在
lib\
sa-jdi.jar 下,
打开工具:
C:\software\jdk8\bin\java.exe -cp C:\software\jdk8\lib\sa-jdi.jar sun.jvm.hotspot.HSDB
然后左上角 File/Attach to hotspot process 输入 Java 进程ID,然后左上角 Tools/Class Browser 找到需要查看的目标地址 0x00000007c0060828 ,下面 Class 信息。
然后依次点击 Tools -> Inspector 输入对象地址
0x00000007c0060828
,回车就能看到 klass 对象信息。
InstanceKlass: public Klass 运行时生成的对象信息。
2.
Oop
而每个 Java 对象在 Hotspot 中被映射成一个 Oop 对象,
Oop结构里面的class如下,代码见
oopsHierarchy.hpp:
typedef juint narrowOop;//java对象中oop的偏移量而
typedef juint narrowKlass;// 如果压缩klass指针,则使用窄klass
typedef void* OopOrNarrowOopStar;
typedef class markOopDesc* markOop;//对象头标记
#ifndef CHECK_UNHANDLED_OOPS
typedef class oopDesc* oop;//对象头(包含markOop),包含Kclass
typedef class instanceOopDesc* instanceOop;//Java类实例对象(包含oop)
typedef class arrayOopDesc* arrayOop;//Java数组(包含oop)
typedef class objArrayOopDesc* objArrayOop;//Java对象数组(包含arrayOop)
typedef class typeArrayOopDesc* typeArrayOop;//Java基本类型数组(包含arrayOop)
#else
Java类对象实例,instanceOopDesc 结构如下,代码见 instancOop.hpp:
class oopDesc {
friend class VMStructs;
private:
volatile markOop _mark;
union _metadata {
Klass* _klass;
narrowKlass _compressed_klass;
} _metadata;
// 省略
};
class instanceOopDesc : public oopDesc {
public:
// aligned header size.
static int header_size() { return sizeof(instanceOopDesc)/HeapWordSize; }
// If compressed, the offset of the fields of the instance may not be aligned.
static int base_offset_in_bytes() {
// offset computation code breaks if UseCompressedClassPointers
// only is true
return (UseCompressedOops && UseCompressedClassPointers) ?
klass_gap_offset_in_bytes() :
sizeof(instanceOopDesc);
}
static bool contains_field_offset(int offset, int nonstatic_field_size) {
int base_in_bytes = base_offset_in_bytes();
return (offset >= base_in_bytes &&
(offset-base_in_bytes) < nonstatic_field_size * heapOopSize);
}
};
// oopDesc is the top baseclass for objects classes. The {name}Desc classes describe
// the format of Java objects so the fields can be accessed from C++.
// oopDesc is abstract.
// (see oopHierarchy for complete oop class hierarchy)
// no virtual functions allowed
也就是说 oopDesc 是所有对象类的顶级基础类,Java 所有对象都转换为 C++ 下 oopDesc 类对象,以此通过 C++ 方法来访问 Java 对象。虚函数不在这里面交给 klass 了。
3.MetaData
方法区 :
是 JVM 的规范,所有虚拟机 必须遵守的。常见的JVM 虚拟机 Hotspot 、 JRockit(Oracle)、J9(IBM)
PermGen space(永久代) 则是 HotSpot 虚拟机 基于 JVM 规范对 方法区 的一个落地实现, 并且只有 HotSpot 才有 PermGen space。
而如 JRockit(Oracle)、J9(IBM) 虚拟机有 方法区 ,但是就没有 PermGen space。
PermGen space 是 JDK7及之前, HotSpot 虚拟机 对 方法区 的一个落地实现。在JDK8被移除。
Metaspace(元空间)是 JDK8及之后, HotSpot 虚拟机 对 方法区 的新的实现。
由于方法区主要存储类的相关信息,所以对于动态生成类的情况比较容易出现永久代的内存溢出。而类加载解析后就保存在 MetaData 中。
MetaData实际上就是HotSpot对方法区实现的,代码见 oopsHierarchy.hpp:
// class MetaspaceObj// 元数据层次结构与oop层次结构是分开的
class ConstMethod;//继承自MetaspaceObj,方法信息相关,参数,返回值,注解,异常,code指令
class ConstantPoolCache;//继承自MetaspaceObj,包含ConstantPool
class MethodData;//继承自Metadata,与方法统计优化有关
// class Metadata
class Method;//继承自Metadata,组织方法有关信息,包含ConstMethod,MethodData,MethodData,CompiledMethod等
class ConstantPool;//继承自Metadata,包含Java类字节码常量池信息
// class CHeapObj
class CompiledICHolder;//继承自CHeapObj,包含方法的编译等信息
4.类解析
入口也就是类加载函数中 ClassLoader::load_classfile 函数中:
//从第一个ClassPathEntry开始遍历所有的ClassPathEntry,也就是从多个类路径下进行查找
stream = e->open_stream(file_name, CHECK_NULL);
// 如果检查返回false则返回null,check()函数默认返回true
if (!context.check(stream, classpath_index)) {
return h; // NULL
}
// 构建一个ClassFileParser实例用于 parse 解析
ClassFileParser parser(stream);
// 构建一个ClassLoaderData实例
ClassLoaderData* loader_data = ClassLoaderData::the_null_class_loader_data();
//解析并加载class文件,注意此时并未开始链接
instanceKlassHandle result = parser.parseClassFile(h_name,
loader_data,
protection_domain,
parsed_name,
context.should_verify(classpath_index),
THREAD);
接下来是类解析函数,涉及到常量池解析、接口解析、类字段解析、方法解析、虚表共和mirandas处理、解析完毕最终创建 InstanceKlass 对象。
创建实例对象时,虚拟机会从instanceKlass中读取Java类所需要的堆内存大小并分配对象的内存空间。
类解析的主要步骤:
加载类文件stream,ClassFileStream* cfs = stream(),取类名称,版本信息、标记是否需要验证。
解析常量池,统计Java类中非静态字段的总数量,按照5大类型分别统计,包含 字符串、final变量值、类名、方法名。调用 parse_constant_pool() 函数。
会计算Java类字段的起始偏移量,其实偏移位置从父类继承的字段域末尾开始,
按照分配策略,计算每一个类型的其实偏移量来
计算Java类在堆内存中所需要的内存空间。
解析父类,调用的是 parse_super_class()。
解析字段,parse_fields()。
解析方法 ,parse_methods()。
解析属性,parse_classfile_attributes()。
创建 InstanceKlass,调用 allocate_instance_klass()。
创建 InstanceMirrorKlass,调用 create_mirror(),表示 java.lang.Class 的特殊对象用于反射。
源代码如下:
classFileParser.parseClassFile()
instanceKlassHandle ClassFileParser::parseClassFile(Symbol* name,
ClassLoaderData* loader_data,
Handle protection_domain,
KlassHandle host_klass,
GrowableArray<Handle>* cp_patches,
TempNewSymbol& parsed_name,
bool verify,
TRAPS) {
// 省略
// 加载类文件stream
ClassFileStream* cfs = stream();
// Timing
assert(THREAD->is_Java_thread(), "must be a JavaThread");
JavaThread* jt = (JavaThread*) THREAD;
PerfClassTraceTime ctimer(ClassLoader::perf_class_parse_time(),
ClassLoader::perf_class_parse_selftime(),
NULL,
jt->get_thread_stat()->perf_recursion_counts_addr(),
jt->get_thread_stat()->perf_timers_addr(),
PerfClassTraceTime::PARSE_CLASS);
init_parsed_class_attributes(loader_data);
// 省略
// 标记需要验证
cfs->set_verify(_need_verify);
// 类名
_class_name = (name != NULL) ? name : vmSymbols::unknown_class_name();
cfs->guarantee_more(8, CHECK_(nullHandle)); // magic, major, minor
// 魔数
u4 magic = cfs->get_u4_fast();
guarantee_property(magic == JAVA_CLASSFILE_MAGIC,"Incompatible magic value %u in class file %s",
magic, CHECK_(nullHandle));
// 版本字段
u2 minor_version = cfs->get_u2_fast();
u2 major_version = cfs->get_u2_fast();
// 省略
// Constant pool 解析常量池
// 常量池存放文件字符串、final变量值、类名、方法名等常量
constantPoolHandle cp = parse_constant_pool(CHECK_(nullHandle));
// 省略
// 解析父类
u2 super_class_index = cfs->get_u2_fast();
instanceKlassHandle super_klass = parse_super_class(super_class_index,CHECK_NULL);
// 解析接口
u2 itfs_len = cfs->get_u2_fast();
Array<Klass*>* local_interfaces = parse_interfaces(itfs_len, protection_domain, _class_name,
&has_default_methods, CHECK_(nullHandle));
// 解析字段
u2 java_fields_count = 0;
FieldAllocationCount fac;
Array<u2>* fields = parse_fields(class_name,
access_flags.is_interface(),
&fac, &java_fields_count,
CHECK_(nullHandle));
// 解析方法
bool has_final_method = false;
AccessFlags promoted_flags;
promoted_flags.set_flags(0);
Array<Method*>* methods = parse_methods(access_flags.is_interface(),
&promoted_flags,
&has_final_method,
&declares_default_methods,
CHECK_(nullHandle));
if (declares_default_methods) {
has_default_methods = true;
}
// 解析类属性
// Additional attributes
ClassAnnotationCollector parsed_annotations;
parse_classfile_attributes(&parsed_annotations, CHECK_(nullHandle));
// 省略
// save super klass for error handling.
_super_klass = super_klass;
// Compute the transitive list of all unique interfaces implemented by this class
_transitive_interfaces =
compute_transitive_interfaces(super_klass, local_interfaces, CHECK_(nullHandle));
// 这里主要是用于vtable的构造和快速查找吧
intArray* method_ordering = sort_methods(methods);
// 省略
// 解析完毕创建 InstanceKlass 实例,InstanceKlass 是保存在方法区的
// 需要传入itable与vtable的大小,另外还需要传入static_field_size与OopMapBlock。
// 这些都是在创建相关对象时计算内存占用大小所必须的参数。
_klass = InstanceKlass::allocate_instance_klass(loader_data,
vtable_size,
itable_size,
info.static_field_size,
total_oop_map_size2,
rt,
access_flags,
name,
super_klass(),
!host_klass.is_null(),
CHECK_(nullHandle));
instanceKlassHandle this_klass (THREAD, _klass);
// 省略
// InstanceMirrorKlass对象用于表示特殊的java.lang.Class类
// 指向java.lang.Class的oop,也就是 Java 类对象实例,放在堆中,堆也就是GC的主要区域
java_lang_Class::create_mirror(this_klass, class_loader, protection_domain,CHECK_(nullHandle));
// Generate any default methods - default methods are interface methods
// that have a default implementation. This is new with Lambda project.
if (has_default_methods ) {
DefaultMethods::generate_default_methods(
this_klass(), &all_mirandas, CHECK_(nullHandle));
}
record_defined_class_dependencies(this_klass, CHECK_NULL);
ClassLoadingService::notify_class_loaded(InstanceKlass::cast(this_klass()),false /* not shared class */);
// 省略
instanceKlassHandle this_klass (THREAD, preserve_this_klass);
debug_only(this_klass->verify();)
_klass = NULL;
return this_klass;
}
类文件可以通过idea 插件 jclasslib bytecode viewer 进行查看。
5.类链接
再回到类加载解析的入口函数,在执行完类加载之后就会执行类链接:
接着调用引导类加载器进行加载后,将得到的 Klass 对象缓存到 SystemDictionary::_dictionary中:
instanceKlassHandle SystemDictionary::load_instance_class(Symbol* class_name, Handle class_loader, TRAPS) {
instanceKlassHandle nh = instanceKlassHandle(); // null Handle
// 默认的加载方式
if (class_loader.is_null()) {
// 省略
// 使用引导类加载器进行类加载
if (k.is_null()) {
// Use VM class loader
PerfTraceTime vmtimer(ClassLoader::perf_sys_classload_time());
k = ClassLoader::load_classfile(class_name, CHECK_(nh));
}
// 将 Klass实例添加到字典中
if (!k.is_null()) {
k = find_or_define_instance_class(class_name, class_loader, k, CHECK_(nh));
}
return k;
}
// 使用指定的类加载器加载,最终会调用java.lang.ClassLoader类中的loadClass()方法执行类加载
else {
// 省略
return nh;
}
}
SystemDictionary::find_or_define_instance_class() =>
SystemDictionary::define_instance_class() => 类初始化
InstanceKlass::eager_initialize() => 类链接
InstanceKlass::eager_initialize_impl() => 类连接
InstanceKlass::link_class_impl() => 链接接口方法、类方法
bool InstanceKlass::link_class_impl(instanceKlassHandle this_oop, bool throw_verifyerror, TRAPS) {
if (this_oop->is_in_error_state()) {
ResourceMark rm(THREAD);
THROW_MSG_(vmSymbols::java_lang_NoClassDefFoundError(),this_oop->external_name(), false);
}
if (this_oop->is_linked()) {
return true;
}
assert(THREAD->is_Java_thread(), "non-JavaThread in link_class_impl");
JavaThread* jt = (JavaThread*)THREAD;
// 先链接父类
instanceKlassHandle super(THREAD, this_oop->super());
if (super.not_null()) {
if (super->is_interface()) { // check if super class is an interface
ResourceMark rm(THREAD);
Exceptions::fthrow(
THREAD_AND_LOCATION,
vmSymbols::java_lang_IncompatibleClassChangeError(),
"class %s has interface %s as super class",
this_oop->external_name(),
super->external_name()
);
return false;
}
link_class_impl(super, throw_verifyerror, CHECK_false);
}
// 先链接所有接口 Klass
Array<Klass*>* interfaces = this_oop->local_interfaces();
int num_interfaces = interfaces->length();
for (int index = 0; index < num_interfaces; index++) {
HandleMark hm(THREAD);
instanceKlassHandle ih(THREAD, interfaces->at(index));
link_class_impl(ih, throw_verifyerror, CHECK_false);
}
if (this_oop->is_linked()) {
return true;
}
PerfClassTraceTime vmtimer(ClassLoader::perf_class_link_time(),
ClassLoader::perf_class_link_selftime(),
ClassLoader::perf_classes_linked(),
jt->get_thread_stat()->perf_recursion_counts_addr(),
jt->get_thread_stat()->perf_timers_addr(),
PerfClassTraceTime::CLASS_LINK);
// 链接和重写
{
oop init_lock = this_oop->init_lock();
ObjectLocker ol(init_lock, THREAD, init_lock != NULL);
if (!this_oop->is_linked()) {
if (!this_oop->is_rewritten()) {
{
PerfClassTraceTime timer(ClassLoader::perf_class_verify_time(),
ClassLoader::perf_class_verify_selftime(),
ClassLoader::perf_classes_verified(),
jt->get_thread_stat()->perf_recursion_counts_addr(),
jt->get_thread_stat()->perf_timers_addr(),
PerfClassTraceTime::CLASS_VERIFY);
bool verify_ok = verify_code(this_oop, throw_verifyerror, THREAD);
if (!verify_ok) {
return false;
}
}
if (this_oop->is_linked()) {
return true;
}
this_oop->rewrite_class(CHECK_false);
}
// 链接所有方法
this_oop->link_methods(CHECK_false);
if (!this_oop()->is_shared()) {
ResourceMark rm(THREAD);
this_oop->vtable()->initialize_vtable(true, CHECK_false);
this_oop->itable()->initialize_itable(true, CHECK_false);
}
#ifdef ASSERT
else {
ResourceMark rm(THREAD);
this_oop->vtable()->verify(tty, true);
}
#endif
this_oop->set_init_state(linked);
if (JvmtiExport::should_post_class_prepare()) {
Thread *thread = THREAD;
assert(thread->is_Java_thread(), "thread->is_Java_thread()");
JvmtiExport::post_class_prepare((JavaThread *) thread, this_oop());
}
}
}
return true;
}