從 JDK 原始碼角度看 Object
Java的Object是所有其他類的父類,從繼承的層次來看它就是最頂層根,所以它也是唯一一個沒有父類的類。它包含了物件常用的一些方法,比如getClass、hashCode、equals、clone、toString、notify、wait等常用方法。所以其他類繼承了Object後就可以不用重複實現這些方法。這些方法大多數是native方法,下面具體分析。
主要的程式碼如下:
publicclassObject{privatestaticnativevoidregisterNatives();static{ registerNatives(); }publicfinalnativeClass getClass();publicnativeinthashCode();publicbooleanequals(Object obj){return(this== obj); }protectednativeObjectclone()throwsCloneNotSupportedException;publicStringtoString(){returngetClass().getName() +"@"+ Integer.toHexString(hashCode()); }publicfinalnativevoidnotify();publicfinalnativevoidnotifyAll();publicfinalnativevoidwait(longtimeout)throwsInterruptedException;publicfinalvoidwait(longtimeout,intnanos)throwsInterruptedException{if(timeout <0) {thrownewIllegalArgumentException("timeout value is negative"); }if(nanos <0|| nanos >999999) {thrownewIllegalArgumentException("nanosecond timeout value out of range"); }if(nanos >0) { timeout++; } wait(timeout); }publicfinalvoidwait()throwsInterruptedException{ wait(0); }protectedvoidfinalize()throwsThrowable{}}
registerNatives方法
由於registerNatives方法被static塊修飾,所以在載入Object類時就會執行該方法,對應的本地方法為Java_java_lang_Object_registerNatives,如下,
JNIEXPORTvoidJNICALLJava_java_lang_Object_registerNatives(JNIEnv *env, jclass cls){ (*env)->RegisterNatives(env, cls, methods,sizeof(methods)/sizeof(methods[0]));}
可以看到它間接呼叫了JNINativeInterface_結構體的方法,簡單可以看成是這樣:它乾的事大概就是將Java層的方法名和本地函式對應起來,方便執行引擎在執行位元組碼時根據這些對應關係表來呼叫C/C++函式,如下面,將這些方法進行註冊,執行引擎執行到hashCode方法時就可以通過關係表來查詢到JVM的JVM_IHashCode函式,其中()I還可以得知Java層上的型別應該轉為int型別。這個對映其實就可以看成將字串對映到函式指標。
staticJNINativeMethod methods[] = { {"hashCode","()I", (void*)&JVM_IHashCode}, {"wait","(J)V", (void*)&JVM_MonitorWait}, {"notify","()V", (void*)&JVM_MonitorNotify}, {"notifyAll","()V", (void*)&JVM_MonitorNotifyAll}, {"clone","()Ljava/lang/Object;", (void*)&JVM_Clone},};
getClass方法
getClass方法也是個本地方法,對應的本地方法為Java_java_lang_Object_getClass,如下:
JNIEXPORT jclass JNICALLJava_java_lang_Object_getClass(JNIEnv *env, jobjectthis){if(this==NULL) { JNU_ThrowNullPointerException(env,NULL);return0; }else{return(*env)->GetObjectClass(env,this); }}
所以這裡主要就是看GetObjectClass函數了,Java層的Class在C++層與之對應的則是klassOop,所以關於類的元資料和方法資訊可以通過它獲得。
JNI_ENTRY(jclass, jni_GetObjectClass(JNIEnv *env, jobject obj)) JNIWrapper("GetObjectClass"); DTRACE_PROBE2(hotspot_jni, GetObjectClass__entry, env, obj); klassOop k = JNIHandles::resolve_non_null(obj)->klass(); jclass ret =(jclass)JNIHandles::make_local(env, Klass::cast(k)->java_mirror()); DTRACE_PROBE1(hotspot_jni, GetObjectClass__return, ret); return ret;JNI_END
hashCode方法
由前面registerNatives方法將幾個本地方法註冊可知,hashCode方法對應的函式為JVM_IHashCode,即
JVM_ENTRY(jint, JVM_IHashCode(JNIEnv* env, jobject handle)) JVMWrapper("JVM_IHashCode");//as implementedinthe classic virtual machine;return0ifobject is NULLreturnhandle == NULL ?0: ObjectSynchronizer::FastHashCode (THREAD, JNIHandles::resolve_non_null(handle)) ;JVM_END
對於hashcode生成的邏輯由synchronizer.cpp的get_next_hash函式決定,實現比較複雜,根據hashcode的不同值有不同的生成策略,最後使用一個hash掩碼處理。
staticinline intptr_t get_next_hash(Thread *Self, oop obj) { intptr_t value =0;if(hashCode ==0) { value = os::random() ; }elseif(hashCode ==1) { intptr_t addrBits = intptr_t(obj) >>3; value = addrBits ^ (addrBits >>5) ^ GVars.stwRandom ; }elseif(hashCode ==2) { value =1;// for sensitivity testing}elseif(hashCode ==3) { value = ++GVars.hcSequence ; }elseif(hashCode ==4) { value = intptr_t(obj) ; }else{ unsigned t =Self->_hashStateX ; t ^= (t <<11) ;Self->_hashStateX =Self->_hashStateY ;Self->_hashStateY =Self->_hashStateZ ;Self->_hashStateZ =Self->_hashStateW ; unsigned v =Self->_hashStateW ; v = (v ^ (v >>19)) ^ (t ^ (t >>8)) ;Self->_hashStateW = v ; value = v ; } value &= markOopDesc::hash_mask;if(value ==0) value =0xBAD; assert (value != markOopDesc::no_hash,"invariant") ; TEVENT (hashCode: GENERATE) ;returnvalue;}
equals方法
這是一個非本地方法,判斷邏輯也十分簡單,直接==比較。
clone方法
由本地方法表知道clone方法對應的本地函式為JVM_Clone,clone方法主要實現物件的克隆功能,根據該物件生成一個相同的新物件(我們常見的類的物件的屬性如果是原始型別則會克隆值,但如果是物件則會克隆物件的地址)。Java的類要實現克隆則需要實現Cloneable介面,if (!klass->is_cloneable())這裡會校驗是否有實現該介面。然後判斷是否是陣列分兩種情況分配記憶體空間,新物件為new_obj,接著對new_obj進行copy及C++層資料結構的設定。最後再轉成jobject型別方便轉成Java層的Object型別。
JVM_ENTRY(jobject, JVM_Clone(JNIEnv* env, jobject handle)) JVMWrapper("JVM_Clone"); Handle obj(THREAD, JNIHandles::resolve_non_null(handle));constKlassHandle klass (THREAD, obj->klass()); JvmtiVMObjectAllocEventCollector oam;if(!klass->is_cloneable()) { ResourceMark rm(THREAD); THROW_MSG_0(vmSymbols::java_lang_CloneNotSupportedException(), klass->external_name()); }constint size = obj->size(); oop new_obj =NULL;if(obj->is_javaArray()) {constint length = ((arrayOop)obj())->length(); new_obj = CollectedHeap::array_allocate(klass, size, length, CHECK_NULL); }else{ new_obj = CollectedHeap::obj_allocate(klass, size, CHECK_NULL); } Copy::conjoint_jlongs_atomic((jlong*)obj(), (jlong*)new_obj, (size_t)align_object_size(size) / HeapWordsPerLong); new_obj->init_mark(); BarrierSet* bs = Universe::heap()->barrier_set(); assert(bs->has_write_region_opt(),"Barrier set does not have write_region"); bs->write_region(MemRegion((HeapWord*)new_obj, size));if(klass->has_finalizer()) { assert(obj->is_instance(),"should be instanceOop"); new_obj = instanceKlass::register_finalizer(instanceOop(new_obj), CHECK_NULL); }returnJNIHandles::make_local(env, oop(new_obj));JVM_END
toString方法
邏輯是獲取class名稱加上@再加上十六進位制的hashCode。
notify方法
此方法用來喚醒執行緒,final修飾說明不可重寫。與之對應的本地方法為JVM_MonitorNotify,ObjectSynchronizer::notify最終會呼叫ObjectMonitor::notify(TRAPS),這個過程是ObjectSynchronizer會嘗試當前執行緒獲取free ObjectMonitor物件,不成功則嘗試從全域性中獲取。
JVM_ENTRY(void, JVM_MonitorNotify(JNIEnv* env, jobject handle)) JVMWrapper("JVM_MonitorNotify"); Handle obj(THREAD, JNIHandles::resolve_non_null(handle)); assert(obj->is_instance() || obj->is_array(),"JVM_MonitorNotify must apply to an object"); ObjectSynchronizer::notify(obj, CHECK);JVM_END
ObjectMonitor物件包含一個_WaitSet佇列物件,此物件儲存著所有處於wait狀態的執行緒,用ObjectWaiter物件表示。notify要做的事是先獲取_WaitSet佇列鎖,再取出_WaitSet佇列中第一個ObjectWaiter物件,再根據不同策略處理該物件,比如把它加入到_EntryList佇列中。然後再釋放_WaitSet佇列鎖。它並沒有釋放synchronized對應的鎖,所以鎖只能等到synchronized同步塊結束時才釋放。
void ObjectMonitor::notify(TRAPS) { CHECK_OWNER();if(_WaitSet ==NULL) { TEVENT (Empty-Notify) ;return; } DTRACE_MONITOR_PROBE(notify, this, object(), THREAD); int Policy = Knob_MoveNotifyee ; Thread::SpinAcquire (&_WaitSetLock,"WaitSet - notify") ; ObjectWaiter * iterator = DequeueWaiter() ;if(iterator !=NULL) { TEVENT (Notify1 - Transfer) ; guarantee (iterator->TState == ObjectWaiter::TS_WAIT,"invariant") ; guarantee (iterator->_notified ==0,"invariant") ;if(Policy !=4) { iterator->TState = ObjectWaiter::TS_ENTER ; } iterator->_notified =1; ObjectWaiter *List= _EntryList ;if(List!=NULL) { assert (List->_prev ==NULL,"invariant") ; assert (List->TState == ObjectWaiter::TS_ENTER,"invariant") ; assert (List!= iterator,"invariant") ; }if(Policy ==0) {// prepend to EntryListif(List==NULL) { iterator->_next = iterator->_prev =NULL; _EntryList = iterator ; }else{List->_prev = iterator ; iterator->_next =List; iterator->_prev =NULL; _EntryList = iterator ; } }elseif(Policy ==1) {// append to EntryListif(List==NULL) { iterator->_next = iterator->_prev =NULL; _EntryList = iterator ; }else{// CONSIDER: finding the tail currently requires a linear-time walk of// the EntryList. We can make tail access constant-time by converting to// a CDLL instead of using our current DLL.ObjectWaiter * Tail ;for(Tail =List; Tail->_next !=NULL; Tail = Tail->_next) ; assert (Tail !=NULL&& Tail->_next ==NULL,"invariant") ; Tail->_next = iterator ; iterator->_prev = Tail ; iterator->_next =NULL; } }elseif(Policy ==2) {// prepend to cxq// prepend to cxqif(List==NULL) { iterator->_next = iterator->_prev =NULL; _EntryList = iterator ; }else{ iterator->TState = ObjectWaiter::TS_CXQ ;for(;;) { ObjectWaiter * Front = _cxq ; iterator->_next = Front ;if(Atomic::cmpxchg_ptr (iterator, &_cxq, Front) == Front) {break; } } } }elseif(Policy ==3) {// append to cxqiterator->TState = ObjectWaiter::TS_CXQ ;for(;;) { ObjectWaiter * Tail ; Tail = _cxq ;if(Tail ==NULL) { iterator->_next =NULL;if(Atomic::cmpxchg_ptr (iterator, &_cxq,NULL) ==NULL) {break; } }else{while(Tail->_next !=NULL) Tail = Tail->_next ; Tail->_next = iterator ; iterator->_prev = Tail ; iterator->_next =NULL;break; } } }else{ ParkEvent * ev = iterator->_event ; iterator->TState = ObjectWaiter::TS_RUN ; OrderAccess::fence() ; ev->unpark() ; }if(Policy <4) { iterator->wait_reenter_begin(this); }// _WaitSetLock protects the wait queue, not the EntryList. We could// move the add-to-EntryList operation, above, outside the critical section// protected by _WaitSetLock. In practice that's not useful. With the// exception of wait() timeouts and interrupts the monitor owner// is the only thread that grabs _WaitSetLock. There's almost no contention// on _WaitSetLock so it's not profitable to reduce the length of the// critical section.} Thread::SpinRelease (&_WaitSetLock) ;if(iterator !=NULL&& ObjectMonitor::_sync_Notifications !=NULL) { ObjectMonitor::_sync_Notifications->inc() ; }}
推薦一個Java學習交流群:795632998,群內有分散式架構、高效能、高併發、效能優化、Spring boot、Redis、ActiveMQ、Nginx、Netty、Jvm等視訊資料提供學習參考
notifyAll方法
與notify方法類似,只是在取_WaitSet佇列時不是取第一個而是取所有。
wait方法
wait方法是讓執行緒等待,它對應的本地方法是JVM_MonitorWait,間接呼叫了ObjectSynchronizer::wait,與notify對應,它也是對應呼叫ObjectMonitor物件的wait方法。該方法較長,這裡不貼出來了,大概就是建立一個ObjectWaiter物件,接著獲取_WaitSet佇列鎖將ObjectWaiter物件新增到該佇列中,再釋放佇列鎖。另外,它還會釋放synchronized對應的鎖,所以鎖沒有等到synchronized同步塊結束時才釋放。
JVM_ENTRY(void, JVM_MonitorWait(JNIEnv* env, jobject handle, jlong ms)) JVMWrapper("JVM_MonitorWait"); Handle obj(THREAD, JNIHandles::resolve_non_null(handle)); assert(obj->is_instance() || obj->is_array(),"JVM_MonitorWait must apply to an object"); JavaThreadInObjectWaitState jtiows(thread, ms !=0);if(JvmtiExport::should_post_monitor_wait()) { JvmtiExport::post_monitor_wait((JavaThread *)THREAD, (oop)obj(), ms); } ObjectSynchronizer::wait(obj, ms, CHECK);JVM_END
finalize方法
這個方法用於當物件被回收時呼叫,這個由JVM支援,Object的finalize方法預設是什麼都沒有做,如果子類需要在物件被回收時執行一些邏輯處理,則可以重寫finalize方法。