new背后的故事
对象创建
例子
首先看一个例子:
public class BBB {
int id;
String name;
public BBB(int id, String name) {
this.id = id;
this.name = name;
}
}
public class AAA {
public static void main(String[] args){
BBB bb = new BBB(1,"jsy");
}
}
编译之后,用javap -c AAA来看对应的字节码:
➜ javap -c AAA
Compiled from "AAA.java"
public class AAA {
public AAA();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]);
Code:
0: new #2 // class BBB
3: dup
4: iconst_1
5: ldc #3 // String jsy
7: invokespecial #4 // Method BBB."<init>":(ILjava/lang/String;)V
10: astore_1
11: return
}
可以看到,4-8行是构造函数,10-18是main方法。
其中在main方法中,#2 这里new了一个class BBB的对象,这个就是我们需要着重看到的对象创建。
new背后的故事
new关键字对应jvm中的_new指令,实现在interpreterRuntime.cpp中,代码如下:
IRT_ENTRY(void, InterpreterRuntime::_new(JavaThread* thread, ConstantPool* pool, int index))
Klass* k_oop = pool->klass_at(index, CHECK);
instanceKlassHandle klass (THREAD, k_oop);
// Make sure we are not instantiating an abstract klass
klass->check_valid_for_instantiation(true, CHECK);
// Make sure klass is initialized
klass->initialize(CHECK);
// At this point the class may not be fully initialized
// because of recursive initialization. If it is fully
// initialized & has_finalized is not set, we rewrite
// it into its fast version (Note: no locking is needed
// here since this is an atomic byte write and can be
// done more than once).
//
// Note: In case of classes with has_finalized we don't
// rewrite since that saves us an extra check in
// the fast version which then would call the
// slow version anyway (and do a call back into
// Java).
// If we have a breakpoint, then we don't rewrite
// because the _breakpoint bytecode would be lost.
oop obj = klass->allocate_instance(CHECK);
thread->set_vm_result(obj);
IRT_END
其中:
-
pool是指AAA类的constant pool,此时AAA的class已经加载JVM中
-
new之后的#2 代表BBB类的全限定名的符号引用在constant pool中的位置
-
pool —>class_at()负责返回new的对象---BBB类对应的klass对象,实现如下:
Klass* ConstantPool::klass_at_impl(const constantPoolHandle& this_cp, int which, bool save_resolution_error, TRAPS) { assert(THREAD->is_Java_thread(), "must be a Java thread"); // A resolved constantPool entry will contain a Klass*, otherwise a Symbol*. // It is not safe to rely on the tag bit's here, since we don't have a lock, and // the entry and tag is not updated atomicly. CPSlot entry = this_cp->slot_at(which); if (entry.is_resolved()) { assert(entry.get_klass()->is_klass(), "must be"); // Already resolved - return entry. return entry.get_klass(); } // This tag doesn't change back to unresolved class unless at a safepoint. if (this_cp->tag_at(which).is_unresolved_klass_in_error()) { // The original attempt to resolve this constant pool entry failed so find the // class of the original error and throw another error of the same class // (JVMS 5.4.3). // If there is a detail message, pass that detail message to the error. // The JVMS does not strictly require us to duplicate the same detail message, // or any internal exception fields such as cause or stacktrace. But since the // detail message is often a class name or other literal string, we will repeat it // if we can find it in the symbol table. throw_resolution_error(this_cp, which, CHECK_0); ShouldNotReachHere(); }
可以看到,如果常量池中指定位置 #2 的数据已经resolve(分析)并且是klass类型,直接通过entry.get_class返回klass。否则抛出异常。注意这里跟jdk7的实现不同,jdk7的实现如下:
可以看到,这里的klassOop已经被Klass所代替,而且is_oop也被is_resolved替代。
而且如果是unresolve状态,直接抛出异常,不会继续下去,这个跟JDK7 也完全不同
-
klass->check_valid_for_instantiation 防止抽象类被实例化
-
klass->initialize初始化,具体实现在InstanceKlass.cpp中如下:
// See "The Virtual Machine Specification" section 2.16.5 for a detailed explanation of the class initialization // process. The step comments refers to the procedure described in that section. // Note: implementation moved to static method to expose the this pointer. void InstanceKlass::initialize(TRAPS) { if (this->should_be_initialized()) { HandleMark hm(THREAD); instanceKlassHandle this_k(THREAD, this); initialize_impl(this_k, CHECK); // Note: at this point the class may be initialized // OR it may be in the state of being initialized // in case of recursive initialization! } else { assert(is_initialized(), "sanity check"); } }
可看到,如果BBB的instanceKlass对象已经初始化完成,则直接返回。
否则,通过initialize_impl方法进行初始化。代码如下:
void InstanceKlass::initialize_impl(instanceKlassHandle this_k, TRAPS) { // Make sure klass is linked (verified) before initialization // A class could already be verified, since it has been reflected upon. this_k->link_class(CHECK); DTRACE_CLASSINIT_PROBE(required, this_k(), -1); bool wait = false; // refer to the JVM book page 47 for description of steps // Step 1 { oop init_lock = this_k->init_lock(); ObjectLocker ol(init_lock, THREAD, init_lock != NULL); Thread *self = THREAD; // it's passed the current thread // 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(this_k->is_being_initialized() && !this_k->is_reentrant_initialization(self)) { wait = true; ol.waitUninterruptibly(CHECK); } // Step 3 if (this_k->is_being_initialized() && this_k->is_reentrant_initialization(self)) { DTRACE_CLASSINIT_PROBE_WAIT(recursive, this_k(), -1,wait); return; } // Step 4 if (this_k->is_initialized()) { DTRACE_CLASSINIT_PROBE_WAIT(concurrent, this_k(), -1,wait); return; } // Step 5 if (this_k->is_in_error_state()) { DTRACE_CLASSINIT_PROBE_WAIT(erroneous, this_k(), -1,wait); ResourceMark rm(THREAD); const char* desc = "Could not initialize class "; const char* className = this_k->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 this_k->set_init_state(being_initialized); this_k->set_init_thread(self); } // Step 7 // Next, if C is a class rather than an interface, initialize it's super class and super // interfaces. if (!this_k->is_interface()) { Klass* super_klass = this_k->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 && this_k->has_nonstatic_concrete_methods()) { this_k->initialize_super_interfaces(this_k, 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 this_k->set_initialization_state_and_notify(initialization_error, THREAD); CLEAR_PENDING_EXCEPTION; } DTRACE_CLASSINIT_PROBE_WAIT(super__failed, this_k(), -1,wait); THROW_OOP(e()); } } // Look for aot compiled methods for this klass, including class initializer. AOTLoader::load_for_klass(this_k, THREAD); // Step 8 { assert(THREAD->is_Java_thread(), "non-JavaThread in initialize_impl"); JavaThread* jt = (JavaThread*)THREAD; DTRACE_CLASSINIT_PROBE_WAIT(clinit, this_k(), -1,wait); // 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); this_k->call_class_initializer(THREAD); } // Step 9 if (!HAS_PENDING_EXCEPTION) { this_k->set_initialization_state_and_notify(fully_initialized, CHECK); { ResourceMark rm(THREAD); debug_only(this_k->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((JavaThread*)THREAD); { EXCEPTION_MARK; this_k->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((JavaThread*)THREAD); } DTRACE_CLASSINIT_PROBE_WAIT(error, this_k(), -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, this_k(), -1,wait); }
整个初始化算法分为以下11个步骤:
步骤1
oop init_lock = this_k->init_lock(); ObjectLocker ol(init_lock, THREAD, init_lock != NULL); Thread *self = THREAD; // it's passed the current thread
通过ObjectLocker在初始化之前进行加锁,防止多个线程并发初始化。
步骤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(this_k->is_being_initialized() && !this_k->is_reentrant_initialization(self)) { wait = true; ol.waitUninterruptibly(CHECK); }
如果当前instanceKlassHandle正在被初始化,且初始化线程不是当前线程,则等待,并执行ol.waitUninterruptibly(CHECK),等待其他线程初始化完成后通知。
步骤3
if (this_k->is_being_initialized() && this_k->is_reentrant_initialization(self)) { DTRACE_CLASSINIT_PROBE_WAIT(recursive, this_k(), -1,wait); return; }
如果当前instanceKlassHandle正在被初始化,且初始化线程为当前线程,则直接return。
这种场景R大有过举例子:
如果X类有静态变量指向new Y类实例,Y类中又有静态变量指向new X类实例,这样外部在调用X时需要初始化X类,初始化过程中又要触发Y类的初始化,而Y类初始化又再次触发X类的初始化…..
步骤4
if (this_k->is_initialized()) { DTRACE_CLASSINIT_PROBE_WAIT(concurrent, this_k(), -1,wait); return; }
如果已经初始化完成,直接返回。此时为fully_initialized状态
这里补充类的状态:
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 };
步骤5
if (this_k->is_in_error_state()) { DTRACE_CLASSINIT_PROBE_WAIT(erroneous, this_k(), -1,wait); ResourceMark rm(THREAD); const char* desc = "Could not initialize class "; const char* className = this_k->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); } }
如果初始化失败,组装异常信息并抛出异常。
步骤6
this_k->set_init_state(being_initialized); this_k->set_init_thread(self);
设置instanceKlassHandle状态为being_initialized,初始化线程为当前线程
步骤7
// Next, if C is a class rather than an interface, initialize it's super class and super // interfaces. if (!this_k->is_interface()) { Klass* super_klass = this_k->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 && this_k->has_nonstatic_concrete_methods()) { this_k->initialize_super_interfaces(this_k, 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 this_k->set_initialization_state_and_notify(initialization_error, THREAD); CLEAR_PENDING_EXCEPTION; } DTRACE_CLASSINIT_PROBE_WAIT(super__failed, this_k(), -1,wait); THROW_OOP(e()); } } // Look for aot compiled methods for this klass, including class initializer. AOTLoader::load_for_klass(this_k, THREAD);
如果当前InstanceKlassHandle不是接口,并且父类不为空+父类未初始化,初始化其父类
如果当前instanceKlassHandle实现了接口,且此接口定义了non-static方法,那么初始化当前InstanceKlassHandle的动作会触发父接口的初始化。
如果初始化失败,则抛出异常。
步骤8
{ assert(THREAD->is_Java_thread(), "non-JavaThread in initialize_impl"); JavaThread* jt = (JavaThread*)THREAD; DTRACE_CLASSINIT_PROBE_WAIT(clinit, this_k(), -1,wait); // 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); this_k->call_class_initializer(THREAD); }
这里主要调用了call_class_initializer方法,实现如下:
void InstanceKlass::call_class_initializer_impl(instanceKlassHandle this_k, TRAPS) { if (ReplayCompiles && (ReplaySuppressInitializers == 1 || ReplaySuppressInitializers >= 2 && this_k->class_loader() != NULL)) { // Hide the existence of the initializer for the purpose of replaying the compile return; } methodHandle h_method(THREAD, this_k->class_initializer()); assert(!this_k->is_initialized(), "we cannot initialize twice"); if (log_is_enabled(Info, class, init)) { ResourceMark rm; outputStream* log = Log(class, init)::info_stream(); log->print("%d Initializing ", call_class_initializer_impl_counter++); this_k->name()->print_value_on(log); log->print_cr("%s (" INTPTR_FORMAT ")", h_method() == NULL ? "(no method)" : "", p2i(this_k())); } if (h_method() != NULL) { JavaCallArguments args; // No arguments JavaValue result(T_VOID); JavaCalls::call(&result, h_method, &args, CHECK); // Static call (no args) } }
这里最终会执行JavaCalls::call来执行代码块逻辑,再下一层就是具体操作系统的实现了,JavaCalls.cpp中实现如下:
void JavaCalls::call(JavaValue* result, const methodHandle& method, JavaCallArguments* args, TRAPS) { // Check if we need to wrap a potential OS exception handler around thread // This is used for e.g. Win32 structured exception handlers assert(THREAD->is_Java_thread(), "only JavaThreads can make JavaCalls"); // Need to wrap each and every time, since there might be native code down the // stack that has installed its own exception handlers os::os_exception_wrapper(call_helper, result, method, args, THREAD); }
步骤9
if (!HAS_PENDING_EXCEPTION) { this_k->set_initialization_state_and_notify(fully_initialized, CHECK); { ResourceMark rm(THREAD); debug_only(this_k->vtable()->verify(tty, true);) } }
如果初始化过程没有异常,说明已经初始化完成,设置状态为full_initialized,,并通知其他线程初始化已经完成。
否则执行步骤10和11
步骤10&11
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((JavaThread*)THREAD); { EXCEPTION_MARK; this_k->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((JavaThread*)THREAD); } DTRACE_CLASSINIT_PROBE_WAIT(error, this_k(), -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, this_k(), -1,wait); }
如果初始化过程发生异常,则通过set_initialization_state_and_notify设置状态为initialization_error且通知其他线程,抛出异常
-
如果初始化完成,oop obj = klass->allocate_instance(CHECK); 这行代码会在堆内存中创建一个instanceOopDesc对象,即类的实例化
instanceOopDesc的类定义如下:
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); } };
这里指定了头size、类实例占用的内存空间长度、offset内存空间起始值等。
而instanceOopDesc继承自oopDesc,其定义在上面章节OOP。
allocate_instance的实现如下:
instanceOop InstanceKlass::allocate_instance(TRAPS) { bool has_finalizer_flag = has_finalizer(); // Query before possible GC int size = size_helper(); // Query before forming handle. KlassHandle h_k(THREAD, this); instanceOop i; i = (instanceOop)CollectedHeap::obj_allocate(h_k, size, CHECK_NULL); if (has_finalizer_flag && !RegisterFinalizersAtInit) { i = register_finalizer(i, CHECK_NULL); } return i; }
可以看到,这里做了如下操作:
- has_finalizer 判断是否包含不为空的finalize方法
- size_helper 确定需要为当前对象分配多大内存
- CollectedHeap::obj_allocate 在堆中申请指定大小的内存空间,并创建instanceOop对象
- 如果重新实现了finalize方法,那么需要将生成的instanceOop对象包装成Finalizer对象,并将其添加到Finalizer链表中。
- 当对象被GC时,如果是Finalizer对象,会将对象赋值到pending对象,Reference Handler线程会将pending对象push到queue中,Finalizer线程poll到对象,会先删除掉Finalizer链表中的对象,然后再执行对象的finalize方法。
obj_allocate的实现如下:
oop CollectedHeap::obj_allocate(KlassHandle klass, int size, TRAPS) { debug_only(check_for_valid_allocation_state()); assert(!Universe::heap()->is_gc_active(), "Allocation during gc not allowed"); assert(size >= 0, "int won't convert to size_t"); HeapWord* obj = common_mem_allocate_init(klass, size, CHECK_NULL); post_allocation_setup_obj(klass, obj, size); NOT_PRODUCT(Universe::heap()->check_for_bad_heap_word_value(obj, size)); return (oop)obj; }
可以看到,这里创建的对象obj是在Heap中的,通过HeapWord指针记录其信息,然后在返回时转换成instanceOop类型
参考文档:来自战小狼简书