vm初始化后,通过启动类加载器解析出了class文件,并且在堆中分配了java class对象的空间,这仅仅完成了加载,后续还有连接和初始化,才能在runtime使用
参考文档
https://docs.oracle.com/javase/specs/jvms/se17/html/jvms-5.html#jvms-5.3
类被初始化可以有多种方式,主要是这三种
1.Class.forName
最后调用到forName0这个native方法,默认参数initialize为true,会调用clinit
clsss.c
Java_java_lang_Class_forName0
JVM_FindClassFromCaller
jvm.cpp
JVM_FindClassFromCaller
find_class_from_class_loader
SystemDictionary::resolve_or_fail
jclass find_class_from_class_loader(JNIEnv* env, Symbol* name, jboolean init,
Handle loader, Handle protection_domain,
jboolean throwError, TRAPS) {
//类加载已经分析过,这里只会解析出class文件对应的对象并分配好元空间,类型为class InstanceKlass: public Klass
Klass* klass = SystemDictionary::resolve_or_fail(name, loader, protection_domain, throwError != 0, CHECK_NULL);
// Check if we should initialize the class
//判断class.forName传入的是否初始化参数&&已经初始化,调用InstanceKlass的initialize方法
if (init && klass->is_instance_klass()) {
klass->initialize(CHECK_NULL);
}
return (jclass) JNIHandles::make_local(THREAD, klass->java_mirror());
}
2.new关键字
new创建对象如果未被加载过则会触发初始化
代码中的new字段通过Bytecodes映射可以找到187这个代码
具体new这类关键字怎么实现的再起一篇
3.Java_java_lang_ClassLoader_defineClass0
通过Classloader加载class文件的byte数组,最后根据是否需要初始化调用initialize或者link方法
这个口子是给从非jar包方式加载class,比如drools规则生成器,翻译了脚本后生成了代码类,通过define加载运行时的这些类
源码在jvm.cpp中的
jvm_lookup_define_class最后会调用到初始化的方法
if (init) {
ik->initialize(CHECK_NULL);
} else {
ik->link_class(CHECK_NULL);
}
连接与初始化过程
要注意连接之前ClassFileParser解析的时候已经校验过了,把class中用到的类,父类,接口等都加载了
由于加了锁,所以类加载能够保证static代码块这种只会被加载一次
void InstanceKlass::initialize_impl(TRAPS) {
HandleMark hm(THREAD);
// Make sure klass is linked (verified) before initialization
// A class could already be verified, since it has been reflected upon.
/**
* 连接过程大概就是
1.连接父类
2.连接当前类所实现的接口
3.verify_code 校验代码异常相关的信息
4.rewrite_class 暂时没看太明白,感觉是不同虚拟机实现的指令不一样,对class文件中的指令要重写成自己的(字面上还感觉是overriding这种的重写)
5.link方法 ,方法重写了就需要重新关联
jvm specs中最后提到连接会创建数据结构,也可能会oom
*/
link_class(CHECK);
DTRACE_CLASSINIT_PROBE(required, -1);
bool wait = false;
JavaThread* jt = THREAD;
// refer to the JVM book page 47 for description of steps
//上面连接里jvm规范的 5.5. Initialization在描述这些step
// Step 1
{
//对当前线程初始化加锁
Handle h_init_lock(THREAD, init_lock());
ObjectLocker ol(h_init_lock, jt);
// Step 2
// If we were to use wait() instead of waitInterruptibly() then
// we might end up throwing IE from link/symbol resolution sites
// that aren't expected to throw. This would wreak havoc. See 6320309.
while (is_being_initialized() && !is_reentrant_initialization(jt)) {
wait = true;
jt->set_class_to_be_initialized(this);
ol.wait_uninterruptibly(jt);
jt->set_class_to_be_initialized(NULL);
}
// Step 3
//如果锁进到这里一定是当前线程递归的
if (is_being_initialized() && is_reentrant_initialization(jt)) {
DTRACE_CLASSINIT_PROBE_WAIT(recursive, -1, wait);
return;
}
// Step 4
if (is_initialized()) {
DTRACE_CLASSINIT_PROBE_WAIT(concurrent, -1, wait);
return;
}
// Step 5
//初始化出现了错误,不可能成功,直接报错
if (is_in_error_state()) {
DTRACE_CLASSINIT_PROBE_WAIT(erroneous, -1, wait);
ResourceMark rm(THREAD);
const char* desc = "Could not initialize class ";
const char* className = 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);
}
}
// Step 6
//当前线程能够继续执行,解锁(实际代码没看到解锁)
set_init_state(being_initialized);
set_init_thread(jt);
}
// Step 7
// Next, if C is a class rather than an interface, initialize it's super class and super
// interfaces.
// 父类触发初始化,失败就整体抛错
if (!is_interface()) {
Klass* super_klass = super();
if (super_klass != NULL && super_klass->should_be_initialized()) {
//递归调用父类初始化
super_klass->initialize(THREAD);
}
// If C implements any interface that declares a non-static, concrete method,
// the initialization of C triggers initialization of its super interfaces.
// Only need to recurse if has_nonstatic_concrete_methods which includes declaring and
// having a superinterface that declares, non-static, concrete methods
if (!HAS_PENDING_EXCEPTION && has_nonstatic_concrete_methods()) {
initialize_super_interfaces(THREAD);
}
// If any exceptions, complete abruptly, throwing the same exception as above.
if (HAS_PENDING_EXCEPTION) {
Handle e(THREAD, PENDING_EXCEPTION);
CLEAR_PENDING_EXCEPTION;
{
EXCEPTION_MARK;
// Locks object, set state, and notify all waiting threads
set_initialization_state_and_notify(initialization_error, THREAD);
CLEAR_PENDING_EXCEPTION;
}
DTRACE_CLASSINIT_PROBE_WAIT(super__failed, -1, wait);
THROW_OOP(e());
}
}
//规范上step 8说的是断言判断,这里直接就是第九步
// Step 8
{
DTRACE_CLASSINIT_PROBE_WAIT(clinit, -1, wait);
if (class_initializer() != NULL) {
// Timer includes any side effects of class initialization (resolution,
// etc), but not recursive entry into call_class_initializer().
PerfClassTraceTime timer(ClassLoader::perf_class_init_time(),
ClassLoader::perf_class_init_selftime(),
ClassLoader::perf_classes_inited(),
jt->get_thread_stat()->perf_recursion_counts_addr(),
jt->get_thread_stat()->perf_timers_addr(),
PerfClassTraceTime::CLASS_CLINIT);
//调用clinit
call_class_initializer(THREAD);
} else {
// The elapsed time is so small it's not worth counting.
if (UsePerfData) {
ClassLoader::perf_classes_inited()->inc();
}
call_class_initializer(THREAD);
}
}
// Step 9
//通知类初始化完成
if (!HAS_PENDING_EXCEPTION) {
set_initialization_state_and_notify(fully_initialized, CHECK);
{
debug_only(vtable().verify(tty, true);)
}
}
else {
// Step 10 and 11
Handle e(THREAD, PENDING_EXCEPTION);
CLEAR_PENDING_EXCEPTION;
// JVMTI has already reported the pending exception
// JVMTI internal flag reset is needed in order to report ExceptionInInitializerError
JvmtiExport::clear_detected_exception(jt);
{
EXCEPTION_MARK;
set_initialization_state_and_notify(initialization_error, THREAD);
CLEAR_PENDING_EXCEPTION; // ignore any exception thrown, class initialization error is thrown below
// JVMTI has already reported the pending exception
// JVMTI internal flag reset is needed in order to report ExceptionInInitializerError
JvmtiExport::clear_detected_exception(jt);
}
DTRACE_CLASSINIT_PROBE_WAIT(error, -1, wait);
if (e->is_a(vmClasses::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, -1, wait);
}
call_class_initializer获取的就是clinit方法(这个方法在javac编译class的时候都会添加),通过javaCall调用
void InstanceKlass::call_class_initializer(TRAPS) {
if (ReplayCompiles &&
(ReplaySuppressInitializers == 1 ||
(ReplaySuppressInitializers >= 2 && class_loader() != NULL))) {
// Hide the existence of the initializer for the purpose of replaying the compile
return;
}
methodHandle h_method(THREAD, class_initializer());
assert(!is_initialized(), "we cannot initialize twice");
LogTarget(Info, class, init) lt;
if (lt.is_enabled()) {
ResourceMark rm(THREAD);
LogStream ls(lt);
ls.print("%d Initializing ", call_class_initializer_counter++);
name()->print_value_on(&ls);
ls.print_cr("%s (" INTPTR_FORMAT ")", h_method() == NULL ? "(no method)" : "", p2i(this));
}
if (h_method() != NULL) {
JavaCallArguments args; // No arguments
JavaValue result(T_VOID);
JavaCalls::call(&result, h_method, &args, CHECK); // Static call (no args)
}
}