建立一個普通對象,相似執行A a=new A()
這條語句,經過反編譯javap -c
能夠獲得對應指令以下html
0: new #2 // class main/proxy/A
3: dup
4: invokespecial #3 // Method main/proxy/A."<init>":()V
複製代碼
new/dup/invokespecial分別對應虛擬機的指令,後面跟隨的#
表示常量池中的索引java
對象建立完整過程在hotspot中的源碼中可見 bytecodeInterpreter.cppgit
當讀取到_new
指令時,執行以下github
CASE(_new): {
//獲取常量池中的位置
u2 index = Bytes::get_Java_u2(pc+1);
//獲取常量池
constantPoolOop constants = istate->method()->constants();
if (!constants->tag_at(index).is_unresolved_klass()) {
//常量池中已經加載了要新建的對象
...
UPDATE_PC_AND_TOS_AND_CONTINUE(3, 1);
...
}
//常量池中沒有加載要新建的對象,執行加載流程
CALL_VM(InterpreterRuntime::_new(THREAD, METHOD->constants(), index),
handle_exception);
SET_STACK_OBJECT(THREAD->vm_result(), 0);
THREAD->set_vm_result(NULL);
UPDATE_PC_AND_TOS_AND_CONTINUE(3, 1);
}
複製代碼
constantPoolOop
是個存放class常量的數組。class由class file規則定義。constantPoolOop中的大多數實例都是在class解析的時候就放入了數組
確保對象所屬類型已經通過初始化階段bash
if ( ik->is_initialized() && ik->can_be_fastpath_allocated() ) {
...
}
複製代碼
開始執行新建。oracle
//獲取對象的大小
size_t obj_size = ik->size_helper();
oop result = NULL;
// 記錄是否要將全部的字段置0值
bool need_zero = !ZeroTLAB;
//是否在TLAB中分配對象
if (UseTLAB) {
result = (oop) THREAD->tlab().allocate(obj_size);
}
if (result == NULL) {
need_zero = true;
// 直接在eden中分配空間,失敗就重試,直到成功
retry:
HeapWord* compare_to = *Universe::heap()->top_addr();
HeapWord* new_top = compare_to + obj_size;
if (new_top <= *Universe::heap()->end_addr()) {
if (Atomic::cmpxchg_ptr(new_top, Universe::heap()->top_addr(), compare_to) != compare_to) {
goto retry;
}
result = (oop) compare_to;
}
}
複製代碼
if (need_zero ) {
HeapWord* to_zero = (HeapWord*) result + sizeof(oopDesc) / oopSize;
obj_size -= sizeof(oopDesc) / oopSize;
if (obj_size > 0 ) {
memset(to_zero, 0, obj_size * HeapWordSize);
}
}
複製代碼
if (UseBiasedLocking) {
result->set_mark(ik->prototype_header());
} else {
result->set_mark(markOopDesc::prototype());
}
複製代碼
它是由運行時開始執行新建app
IRT_ENTRY(void, InterpreterRuntime::_new(JavaThread* thread, constantPoolOopDesc* pool, int index))
//在常量池中找到對應的klass
klassOop k_oop = pool->klass_at(index, CHECK);
instanceKlassHandle klass (THREAD, k_oop);
// 確保不是抽象函數
klass->check_valid_for_instantiation(true, CHECK);
//執行初始化,在instanceKlass的class中
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
複製代碼
- CHECK 屬於宏定義,實際上表示的是線程
- instanceKlassHandle 屬於宏定義,由
DEF_KLASS_HANDLE
定義,它重載了->
實際執行的方法就是instanceKlass
自己對應的方法
initialize的核心實如今initialize_impl
,在初始化以前首先要確保link
完成,若是沒有則開始驗證jvm
bool instanceKlass::link_class_impl(
instanceKlassHandle this_oop, bool throw_verifyerror, TRAPS) {
...
//1.
instanceKlassHandle super(THREAD, this_oop->super());
if (super.not_null()) {
//執行父類的 link_class_impl
}
//2.
objArrayHandle interfaces (THREAD, this_oop->local_interfaces());
int num_interfaces = interfaces->length();
for (int index = 0; index < num_interfaces; index++) {
//執行每個接口的link_class_impl
}
...
//3.
bool verify_ok = verify_code(this_oop, throw_verifyerror, THREAD);
...
//重寫類的方法的全部字節碼。這必須發生在驗證以後,並且在類的第一個方法執行以前,同時只能執行一次
this_oop->rewrite_class(CHECK_false);
...
//4.
this_oop->relocate_and_link_methods(CHECK_false);
//5.
this_oop->set_init_state(linked);
...
}
複製代碼
當沒有執行link的時候,開始按照以下步驟執行函數
就是鏈接過程當中的驗證、準備、解析
Bytecodes::_invokespecial :
指令。若是發現這個類沒有加載過,則會執行加載對應字節碼的流程link的全部狀態以下
enum ClassState { unparsable_by_gc = 0, // object is not yet parsable by gc. Value of _init_state at object allocation. 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 }; 複製代碼
link完成以後開始執行真正的初始化
initialization_error
,釋放鎖,並拋出NoClassDefFoundError
being_initialized
,並釋放鎖。而後按照每一個字段在ClassFile中出現的順序,一個個的按照類的ConstantValue屬性中的值初始化 新建類的 final static字段ExceptionInInitializerError
來包裝扔出來的異常,若是因爲OOM致使沒法建立ExceptionInInitializerError,則會拋出OOM。在拋出去以前,獲取鎖,標記異常,喚醒全部其餘的線程,並釋放鎖自此 klass->initialize(CHECK);
執行完畢。開始在堆上分配內存
instanceOop instanceKlass::allocate_instance(TRAPS) {
assert(!oop_is_instanceMirror(), "wrong allocation path");
bool has_finalizer_flag = has_finalizer(); // Query before possible GC
//獲取大小
int size = size_helper(); // Query before forming handle.
KlassHandle h_k(THREAD, as_klassOop());
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;
}
複製代碼
自此初始化結束
執行以下
CASE(_dup): /* Duplicate the top item on the stack */
dup(topOfStack);
UPDATE_PC_AND_TOS_AND_CONTINUE(1, 1);
複製代碼
本質上,就是拷貝
tos[Interpreter::expr_index_at(-to_offset)] =
(intptr_t)tos[Interpreter::expr_index_at(-from_offset)];
複製代碼
關鍵部分以下
CASE(_invokespecial):
CASE(_invokestatic): {
u2 index = Bytes::get_native_u2(pc+1);
ConstantPoolCacheEntry* cache = cp->entry_at(index);
...
if ((Bytecodes::Code)opcode == Bytecodes::_invokespecial) {
CHECK_NULL(STACK_OBJECT(-(cache->parameter_size())));
}
//這裏會找到對應的方法執行,f1對於不一樣的類型有不一樣的實現,對於 invokespecial指令來講,它就是 <init> 方法
callee = (methodOop) cache->f1();
...
//返回
UPDATE_PC_AND_RETURN(0);
複製代碼
特殊方法:在java虛擬機中,全部的構造函數都擁有一個同樣的特殊名字<init>
,它由編譯器提供,因爲名字自己是非法的,因此沒法經過java語言來寫,要去執行它只能經過JVM的指令invokespecial
,而且只會在沒有初始化的實例上執行。
<cinit>對比<init>,<cinit>不是初始化方法,不會被JVM指令執行。一樣的它也並非一個合法的名字,名字自己由編譯器提供,<cinit>的執行是屬於初始化流程的一部分。
<cinit>是由編譯器自動收集類中的全部變量的賦值動做和靜態語句塊中的語句合併產生的。
固然這也意味着若是沒有這些,在生成字節碼的時候也能夠不生成這些方法
僅從對應的字節指令解析開始
獲取到了指令以後,跳轉到run開始執行解析
//883行
run:
...
//892行
while(1){
...
opcode = *pc
...
switch(opcode){
CASE(_new):{
...
UPDATE_PC_AND_TOS_AND_CONTINUE(3, 1);
}
...
}
...
do_continue: ;
}
複製代碼
它自己是個宏定義
#undef CASE
#ifdef USELABELS
#define CASE(opcode) opc ## opcode
#define DEFAULT opc_default
#else
#define CASE(opcode) case Bytecodes:: opcode
#define DEFAULT default
#endif
複製代碼
能夠在Bytecodes.hpp中找到對應的指令
enum Code{
...
_new = 187, // 0xbb
_newarray = 188, // 0xbc
_anewarray = 189, // 0xbd
...
}
複製代碼
它是個宏定義,抽取部分以下
UPDATE_PC_AND_TOS_AND_CONTINUE(opsize, stack) { \
pc += opsize; opcode = *pc; MORE_STACK(stack); \
DO_UPDATE_INSTRUCTION_COUNT(opcode); \
DEBUGGER_SINGLE_STEP_NOTIFY(); \
goto do_continue; \
}
複製代碼