在熟悉《class文件和字节码解析》和《Java对象内存表示机制》了之后,我们对类有了更进一步的认识,那么类文件是如何变成Hotspot中的Klass了?
加载器机制
《类加载机制》一文件中,我们已知道java加载器双亲委派机制,也知道调用loadClass方法可自定义加载类。不管我们在JAVA中通过JNI的方式(ClassLoader JNI接口的实现源码在jdk/src/share/native/java/lang/ClassLoader.c中)或者在虚拟机中直接调用,最终关系都离不开以下几个类:
虚拟机自动加载以java.lang.String为例
加载器初始化
Threads::create_vm()《openJdk的启动流程》中的方法->
init_globals()–>
classLoader_init()–>
ClassLoader::initialize()
加载
Threads::create_vm()->
initialize_class(vmSymbols::java_lang_String(), CHECK_0)–>
SystemDictionary::resolve_or_fail–>
SystemDictionary::resolve_instance_class_or_null->
SystemDictionary::load_instance_class->
ClassLoader::load_classfile->
ClassFileParser::parseClassFile
链接
Threads::create_vm()->
initialize_class(vmSymbols::java_lang_String(), CHECK_0)–>
InstanceKlass::initialize–>
InstanceKlass::link_class->
InstanceKlass::link_class_impl
ClassLoader JNI接口方式
加载
class.getClassLoader().loadClass (java中的方法)->
classLoader.findBootstrapClass–>
SystemDictionary::resolve_or_null–>
SystemDictionary::resolve_instance_class_or_null->
SystemDictionary::load_instance_class->
ClassLoader::load_classfile->
ClassFileParser::parseClassFile
链接
InterpretrRuntime:_new (java 中 new语法,触发)->
InstanceKlass::initialize–>
InstanceKlass::link_class->
InstanceKlass::link_class_impl
ClassLoader
ClassLoader类的定义在classfile/classLoader.hpp中,ClassLoader是用于加载Java核心类文件。
#ClassLoader是一个静态类,里面全是全局的东档
class ClassLoader: AllStatic {
//ClassPathEntry类指针,ClassPathEntry用于表示单个classpath路径,所有的ClassPathEntry实例以链表的形式关联起来,_first_entry表示链表的第一个实例
static ClassPathEntry* _first_entry;
// 表示链表的最后一个实例
static ClassPathEntry* _last_entry;
//ClassPathEntry实例以链表数量
static int _num_entries;
// 用于保存已经加载过的包名
static PackageHashtable* _package_hash_table;
//以下几个方法ClassPathEntry链表相关的操作方法:
//setup_search_path
//contains_entry
//add_to_list
//num_classpath_entrie,
//classpath_entry
//create_class_path_entry
//update_class_path_entry_list
//加载zip文件读取写入等操作的动态链接库
//load_zip_library
//创建虚拟机时的初始化的方法,其调用路径
//Threads::create_vm()《openJdk的启动流程》中的方法->
//init_globals()-->
//classLoader_init()-->
//ClassLoader::initialize()
void ClassLoader::initialize() {
assert(_package_hash_table == NULL, "should have been initialized by now.");
EXCEPTION_MARK;
if (UsePerfData) {
//如果开始性能检测则初始化各计数器
NEWPERFTICKCOUNTER(_perf_accumulated_time, SUN_CLS, "time");
NEWPERFTICKCOUNTER(_perf_class_init_time, SUN_CLS, "classInitTime");
}
//加载读写zip文件的动态链接库
load_zip_library();
//设置加载核心jar包的搜索路径,从系统参数Arguments中获取
//使用参数-XX:+TraceClassPaths会打印出具体
//bootstrap路径
//classpath
//path
setup_bootstrap_search_path();
//如果是惰性启动加载,即启动时不加载rt.jar等文件
if (LazyBootClassLoader) {
//设置meta_index_path,设置完成后会触发对meta_index_path下文件的解析
setup_bootstrap_meta_index();
}
}
//根据类名加载指定类文件的方法
instanceKlassHandle ClassLoader::load_classfile(Symbol* h_name, TRAPS) {
ResourceMark rm(THREAD);
//获取类名
const char* class_name = h_name->as_C_string();
//-XX:+TraceClassLoadingPreorder参数能打印出来
EventMark m("loading class %s", class_name);
ThreadProfilerMark tpm(ThreadProfilerMark::classLoaderRegion);
stringStream st;
st.print_raw(h_name->as_utf8());
st.print_raw(".class");
//获取文件名
const char* file_name = st.as_string();
ClassLoaderExt::Context context(class_name, file_name, THREAD);
//ClassFileStream表示Class文件的字节流
ClassFileStream* stream = NULL;
int classpath_index = 0;
ClassPathEntry* e = NULL;
instanceKlassHandle h;
{
//从第一个ClassPathEntry开始遍历所有的ClassPathEntry
e = _first_entry;
while (e != NULL) {
stream = e->open_stream(file_name, CHECK_NULL);
//如果检查返回false则返回null,check方法默认返回true
if (!context.check(stream, classpath_index)) {
return h; // NULL
}
//如果找到目标文件则跳出循环
if (stream != NULL) {
break;
}
e = e->next();
++classpath_index;
}
}
//如果找到了目标class文件
if (stream != NULL) {
//构建一个ClassFileParser实例
ClassFileParser parser(stream);
//构建一个ClassLoaderData实例
ClassLoaderData* loader_data = ClassLoaderData::the_null_class_loader_data();
Handle protection_domain;
TempNewSymbol parsed_name = NULL;
//解析并加载class文件,注意此时并未开始链接
//-XX:+TraceClassLoading可打出加载好的日志
instanceKlassHandle result = parser.parseClassFile(h_name,loader_data,protection_domain,parsed_name,context.should_verify(classpath_index),THREAD);
//如果解析异常
if (HAS_PENDING_EXCEPTION) {
ResourceMark rm;
if (DumpSharedSpaces) {
//打印异常
tty->print_cr("Preload Error: Failed to load %s", class_name);
}
return h;
}
//调用ClassLoader的add_package方法,把当前类的包名加入到_package_hash_table中
h = context.record_result(classpath_index, e, result, THREAD);
} else {
//没有找到目标文件
if (DumpSharedSpaces) {
tty->print_cr("Preload Warning: Cannot find %s", class_name);
}
}
return h;
}
Symbol
Symbol类的定义位于oops/symbol.hpp中,Symbol表示一个规范化的字符串形式的描述符,如方法Object m(int i, double d, Thread t) {…}对应的方法描述符就是(IDLjava/lang/Thread;)Ljava/lang/Object.关于描述述的概念在《class文件和字节码解析》
//vmSymbols::java_lang_String()就是虚拟机提前创建好的java.lang.String的描述符
class Symbol : MetaspaceObj {
volatile short _refcount;//表示该Symbol的引用计数
unsigned short _length;//UTF-8字符串的长度
int _identity_hash;//hash唯一标识码
jbyte _body[1];//用于存储描述符对应的字符串
//可以操作_body属性的
//byte_at_put(int index, int value)
//打印具体字符串内容
//as_C_string(),as_utf8()
//引用计数相关
//refcount(),increment_refcount(),decrement_refcount()
}
SymbolTable
SymbolTable的定义位于classfile/symbolTable.hpp中,对应于C/C++编译过程的符号表,用于保存管理所有的Symbol实例,StringTable就是Java特有的字符串常量池.用作快速查找字符串
SystemDictionary
SystemDictionary类的定义在classfile/systemDictionary.hpp中,是一个系统字典类,用于保存所有已经加载完成的类。
//SystemDictionary相当于类加载的一个统一入口,同时提供查找已加载的类和加载新的类的服务
class SystemDictionary : AllStatic {
//JVM对不同数据设置不同的hashMap容量,保存在_primelist中,当前hashMap要多大的容量取决于_sdgeneration的值
static int _sdgeneration;
static const int _primelist[_prime_array_size];
//实际保存已加载类的HashMap
//key使用类名加classloader的形式
//dictionary()->compute_hash(name_h, loader_data);
static Dictionary* _dictionary;
//当类加载的过程中临时存储键值对的地方,底层数据结构同Dictionary类
static PlaceholderTable* _placeholders;
//JVM 将类信息加载, 解析成为元数据,并根据是否需要修改,将其分类为 Read-Only 部分和 Read-Write 部分。然后,将这些元数据直接存储在文件系统中,作为所谓的 Shared Archive.
// 共享架构下用于保存已加载类的HashMap
static Dictionary* _shared_dictionary;
// 发生修改的次数,类加载或者删除都会增加该计数器
static int _number_of_modifications;
// 系统类加载器的对象锁
static oop _system_loader_lock_obj;
// 保存类加载器加载约束的HashTable
static LoaderConstraintTable* _loader_constraints;
// 保存类解析错误的HashTable
static ResolutionErrorTable* _resolution_errors;
// Invoke methods (JSR 292)
//保存MethodHandle调用的解析结果
static SymbolPropertyTable* _invoke_method_table;
//系统类加载器的引用
static oop _java_system_loader;
//属性操作的相关方法
//check_constraints,add_placeholder,add_klass,dictionary
//根据类加载器和类名加载类的方法
//resolve_or_fail,resolve_or_null,resolve_super_or_fail
//根据class文件流,类加载器和类名加载类的方法
//parse_stream,resolve_from_stream
//解析类的最终都会进入此方法
instanceKlassHandle SystemDictionary::load_instance_class(Symbol* class_name, Handle class_loader, TRAPS) {
instanceKlassHandle nh = instanceKlassHandle(); // null Handle
//如果class_loader为空,即Java类加载器无法加载该类了
if (class_loader.is_null()) {
instanceKlassHandle k;
//目标类未加载
if (k.is_null()) {
//执行目标类的加载
k = ClassLoader::load_classfile(class_name, CHECK_(nh));
}
//如果已经加载则查找加载的类
//并把其加入到_dictionary中
if (!k.is_null()) {
k = find_or_define_instance_class(class_name, class_loader, k, CHECK_(nh));
}
return k;
} else {
ResourceMark rm(THREAD);
assert(THREAD->is_Java_thread(), "must be a JavaThread");
JavaThread* jt = (JavaThread*) THREAD;
//构建JNI调用的参数,即类名
Handle s = java_lang_String::create_from_symbol(class_name, CHECK_(nh));
Handle string = java_lang_String::externalize_classname(s, CHECK_(nh));
//调用的结果
JavaValue result(T_OBJECT);
//结果的处理器
KlassHandle spec_klass (THREAD, SystemDictionary::ClassLoader_klass());
//调用Java的类加载器加载特定类
if (MustCallLoadClassInternal && has_loadClassInternal()) {
JavaCalls::call_special(&result,
class_loader,
spec_klass,
vmSymbols::loadClassInternal_name(),
vmSymbols::string_class_signature(),
string,
CHECK_(nh));
} else {
JavaCalls::call_virtual(&result,
class_loader,
spec_klass,
vmSymbols::loadClass_name(),
vmSymbols::string_class_signature(),
string,
CHECK_(nh));
}
//从加载结果中获取目标类oop
assert(result.get_type() == T_OBJECT, "just checking");
oop obj = (oop) result.get_jobject();
//检查访问权限
if ((obj != NULL) && !(java_lang_Class::is_primitive(obj))) {
instanceKlassHandle k =
instanceKlassHandle(THREAD, java_lang_Class::as_Klass(obj));
//检查类名是否一致
if (class_name == k->name()) {
return k;
}
}
//类加载失败,返回空对象
return nh;
}
}
}
ClassFileParser&Verifier
ClassFileParser类的定义在classfile/classFileParser.hpp中,类解析器,用来解析class文件,它利用ClassFileStream读取class文件的输入流作为ClassFileParser.parseClassFile核心方法的输入。
Veritier类的定义在classfile/Veritier.hpp中,类验证器。其核心方法是Verifier::verify
InstanceKlass
做为oop-klass二分模型的主要类,不在做详细介绍
InstanceKlass{
void initialize_impl(instanceKlassHandle this_oop, TRAPS) {
//完成此类的链接,如果已链接则会立即返回
this_oop->link_class(CHECK);
DTRACE_CLASSINIT_PROBE(required, InstanceKlass::cast(this_oop()), -1);
bool wait = false;
// Step 1
{
//获取对象锁
oop init_lock = this_oop->init_lock();
ObjectLocker ol(init_lock, THREAD, init_lock != NULL);
Thread *self = THREAD; // it's passed the current thread
// Step 2
//如果正在初始化则等待初始化完成
while(this_oop->is_being_initialized() && !this_oop->is_reentrant_initialization(self)) {
wait = true;
ol.waitUninterruptibly(CHECK);
}
//等待超时返回
if (this_oop->is_being_initialized() && this_oop->is_reentrant_initialization(self)) {
DTRACE_CLASSINIT_PROBE_WAIT(recursive, InstanceKlass::cast(this_oop()), -1,wait);
return;
}
//初始化完成返回
if (this_oop->is_initialized()) {
DTRACE_CLASSINIT_PROBE_WAIT(concurrent, InstanceKlass::cast(this_oop()), -1,wait);
return;
}
//状态异常,抛出异常
if (this_oop->is_in_error_state()) {
DTRACE_CLASSINIT_PROBE_WAIT(erroneous, InstanceKlass::cast(this_oop()), -1,wait);
ResourceMark rm(THREAD);
const char* desc = "Could not initialize class ";
const char* className = this_oop->external_name();
size_t msglen = strlen(desc) + strlen(className) + 1;
char* message = NEW_RESOURCE_ARRAY(char, msglen);
if (NULL == message) {
// Out of memory: can't create detailed error message
THROW_MSG(vmSymbols::java_lang_NoClassDefFoundError(), className);
} else {
jio_snprintf(message, msglen, "%s%s", desc, className);
THROW_MSG(vmSymbols::java_lang_NoClassDefFoundError(), message);
}
}
// 设置状态,初始化进行中
this_oop->set_init_state(being_initialized);
this_oop->set_init_thread(self);
}
// Step 7
//如果不是一个接口而是一个类则需要初始化它的父类
if (!this_oop->is_interface()) {
//获取父类
Klass* super_klass = this_oop->super();
//初始化父类
if (super_klass != NULL && super_klass->should_be_initialized()) {
super_klass->initialize(THREAD);
}
//实现的接口存在默认方法则初始化接口
if (!HAS_PENDING_EXCEPTION && this_oop->has_default_methods()) {
this_oop->initialize_super_interfaces(this_oop, THREAD);
}
//初始化异常,抛出异常
if (HAS_PENDING_EXCEPTION) {
Handle e(THREAD, PENDING_EXCEPTION);
CLEAR_PENDING_EXCEPTION;
{
EXCEPTION_MARK;
this_oop->set_initialization_state_and_notify(initialization_error, THREAD);
CLEAR_PENDING_EXCEPTION;
}
DTRACE_CLASSINIT_PROBE_WAIT(super__failed, InstanceKlass::cast(this_oop()), -1,wait);
THROW_OOP(e());
}
}
// Step 8
{
assert(THREAD->is_Java_thread(), "non-JavaThread in initialize_impl");
JavaThread* jt = (JavaThread*)THREAD;
DTRACE_CLASSINIT_PROBE_WAIT(clinit, InstanceKlass::cast(this_oop()), -1,wait);
//执行静态方法
//final static变量和接口的其值为编译期常数的域首先初始化(在准备阶段执行)
//按文本顺序执,下面三种情况会会javac整理进<cinit>
//类变量的初始化器 static int b = 4;
//静态初始化函数 static {...}
//接口域初始化器 static int a = 4;
this_oop->call_class_initializer(THREAD);
}
// Step 9
if (!HAS_PENDING_EXCEPTION) {
//设置状态初始化完成
this_oop->set_initialization_state_and_notify(fully_initialized, CHECK);
{ ResourceMark rm(THREAD);
debug_only(this_oop->vtable()->verify(tty, true);)
}
}
else {
//初始化失败,抛出异常
Handle e(THREAD, PENDING_EXCEPTION);
CLEAR_PENDING_EXCEPTION;
JvmtiExport::clear_detected_exception((JavaThread*)THREAD);
{
EXCEPTION_MARK;
this_oop->set_initialization_state_and_notify(initialization_error, THREAD);
CLEAR_PENDING_EXCEPTION; // ignore any exception thrown, class initialization error is thrown below
JvmtiExport::clear_detected_exception((JavaThread*)THREAD);
}
DTRACE_CLASSINIT_PROBE_WAIT(error, InstanceKlass::cast(this_oop()), -1,wait);
if (e->is_a(SystemDictionary::Error_klass())) {
THROW_OOP(e());
} else {
JavaCallArguments args(e);
THROW_ARG(vmSymbols::java_lang_ExceptionInInitializerError(),
vmSymbols::throwable_void_signature(),
&args);
}
}
DTRACE_CLASSINIT_PROBE_WAIT(end, InstanceKlass::cast(this_oop()), -1,wait);
}
bool 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()) {
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);
}
//完成当前类实现的所有接口的链接
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;
}
{
//初始化对象锁
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()) {
{
//完成字节码验证
bool verify_ok = verify_code(this_oop, throw_verifyerror, THREAD);
if (!verify_ok) {
return false;
}
}
//再次校验是否验证完成
if (this_oop->is_linked()) {
return true;
}
//重写支持更好的解释器运行性能,向常量池添加缓存cache
//并调整相应字节码的常量池索引,指向cache
this_oop->rewrite_class(CHECK_false);
} else if (this_oop()->is_shared()) {
ResourceMark rm(THREAD);
char* message_buffer;
Handle loader = this_oop()->class_loader();
Handle pd = this_oop()->protection_domain();
//依赖约束检查
bool verified = SystemDictionaryShared::check_verification_dependencies(this_oop(),
loader, pd, &message_buffer, THREAD);
if (!verified) {
THROW_MSG_(vmSymbols::java_lang_VerifyError(), message_buffer, false);
}
}
//重定位,为java方法配置解释器或者编译器入口
//完成方法链接,即方法的入参和返回值的类型的链接
this_oop->link_methods(CHECK_false);
//初始化vtable和itable
ClassLoaderData * loader_data = this_oop->class_loader_data();
if (!(this_oop()->is_shared() &&
loader_data->is_the_null_class_loader_data())) {
ResourceMark rm(THREAD);
this_oop->vtable()->initialize_vtable(true, CHECK_false);
this_oop->itable()->initialize_itable(true, CHECK_false);
}
//设置类的状态为链接完成
this_oop->set_init_state(linked);
if (JvmtiExport::should_post_class_prepare()) {
Thread *thread = THREAD;
assert(thread->is_Java_thread(), "thread->is_Java_thread()");
//发布JVMTI事件
JvmtiExport::post_class_prepare((JavaThread *) thread, this_oop());
}
}
}
return true;
}
}
类的状态转换
- 加载,类加载器加载的过程中会对类文件做格式检查
- 链接,对加载到内存的类文件的二进制数据做约束检查(验证),静态字段设置默认值(准备),将常量池中的符合解析成正确的内存地址调用(解析)
- 初始化,就是类的静态属性的初始化
此过程对象拥有以下7种状态
加载
加载过程主要体现在ClassLoader.load_classfile方法中,其它核心部分为
ClassFileParser.parseClassFile。就是把class文件的内容(有规则的,oop-klass模型)放到内存。
1.使用-XX:+TraceClassLoadingPreorder,会写出
[Loading ClassFileTest from file:/Users/moyao/IdeaProjects/simple/out/production/simple/]
2.类解析器将按虚拟机规范定义从Class文件格式读取数据,并对数据进行加工(此图出自《hotspot实战》,和openjdk8有所出入)
3.使用-XX:+TraceClassLoading,会写出
[Loaded ClassFileTest from file:/Users/moyao/IdeaProjects/simple/out/production/simple/]
4.使用-XX:+TraceClassResolution,会写出,
#哪个类来自哪个文件
RESOLVE ClassFileTest java.lang.Object ClassFileTest.java:1
链接
连接过程主要体现在InstanceKlass.bool link_class_impl方法中,就把各个class串连起来,形成一个整体。
验证
验证阶段,确保类或接口的二进制信息有效性。
使用-XX:+UnlockDiagnosticVMOptions -XX:+VerboseVerification 或者-XX:+TraceClassInitialization,会写出
Verifying class ClassFileTest with new format
Verifying method ClassFileTest.<init>()V
StackMapTable: frame_count = 0
table = {
}
Verifying method ClassFileTest.main([Ljava/lang/String;)V
StackMapTable: frame_count = 0
table = {
}
Verifying method ClassFileTest.getA()I
StackMapTable: frame_count = 0
table = {
}
End class verification for: ClassFileTest
准备
准备阶段的任务是创建类或者接口的静态字段,并用默认值初始化这些字段(final static 声明的会在些阶段设置,其它的只会设置默认值),这个阶段不会执行任何的虚拟机指令,在初始化阶段会有显示的初始化器来初始化这些字段,所以准备阶段不做初始化。
在类创建之后的任何时间,任何时间都可以执行准备,但一定要保证在初始化阶段前完成。
默认值初始化静态字段
ClassFileParser::parseClassFile
java_lang_Class::create_mirror
解析
解析是根据运行时常量池里的符号引用来动态决定具体值的过程,其目标主要是将常量池中的4种符号引用转换成直接引用(运行时实际地址)。
- 类
- 接口
- 字段
- 类方法和接口方法
主要的解析方法
link_class_impl
rewrite_class
初始化
初始化对类和接口来说就是执行它的初始化方法,只有在发生下列行为时,类或者接口才会被初始化:
- 执行new,getstatic,putstatic,invokestatic指令时
- 初次调用方法句柄java.lang.invoke.MethodHanlder实例,该实例的种类是REF_getstatic,REF_putstatic,REF_invokestatic
- 调用Class类或者反射类库中的某些反射方法
- 对类的某个子类进行初始化时
- 该类被选定为Java虚拟机启动时的初始类
主要的初始化方法
InstanceKlass:: initialize_impl
主要参考
《hotspot实战》
《Java虚拟机规范8版》
《Hotspot 类加载、链接和初始化》