Java的Object是全部其餘類的父類,從繼承的層次來看它就是最頂層根,因此它也是惟一一個沒有父類的類。它包含了對象經常使用的一些方法,好比getClass
、hashCode
、equals
、clone
、toString
、notify
、wait
等經常使用方法。因此其餘類繼承了Object後就能夠不用重複實現這些方法。這些方法大多數是native方法,下面具體分析。html
主要的代碼以下:java
public class Object {
private static native void registerNatives();
static {
registerNatives();
}
public final native Class
getClass();
public native int hashCode();
public boolean equals(Object obj) {
return (this == obj);
}
protected native Object clone() throws CloneNotSupportedException;
public String toString() {
return getClass().getName() + "@" + Integer.toHexString(hashCode());
}
public final native void notify();
public final native void notifyAll();
public final native void wait(long timeout) throws InterruptedException;
public final void wait(long timeout, int nanos) throws InterruptedException {
if (timeout < 0) {
throw new IllegalArgumentException("timeout value is negative");
}
if (nanos < 0 || nanos > 999999) {
throw new IllegalArgumentException("nanosecond timeout value out of range");
}
if (nanos > 0) {
timeout++;
}
wait(timeout);
}
public final void wait() throws InterruptedException {
wait(0);
}
protected void finalize() throws Throwable {}
}複製代碼
因爲registerNatives方法被static塊修飾,因此在加載Object類時就會執行該方法,對應的本地方法爲Java_java_lang_Object_registerNatives
,以下,數組
JNIEXPORT void JNICALL
Java_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類型。這個映射其實就能夠當作將字符串映射到函數指針。數據結構
static JNINativeMethod 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方法也是個本地方法,對應的本地方法爲Java_java_lang_Object_getClass
,以下:併發
JNIEXPORT jclass JNICALL
Java_java_lang_Object_getClass(JNIEnv *env, jobject this)
{
if (this == NULL) {
JNU_ThrowNullPointerException(env, NULL);
return 0;
} else {
return (*env)->GetObjectClass(env, this);
}
}複製代碼
因此這裏主要就是看GetObjectClass
函數了,Java層的Class在C++層與之對應的則是klassOop
,因此關於類的元數據和方法信息能夠經過它得到。app
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複製代碼
由前面registerNatives方法將幾個本地方法註冊可知,hashCode方法對應的函數爲JVM_IHashCode
,即dom
JVM_ENTRY(jint, JVM_IHashCode(JNIEnv* env, jobject handle))
JVMWrapper("JVM_IHashCode");
// as implemented in the classic virtual machine; return 0 if object is NULL
return handle == NULL ? 0 : ObjectSynchronizer::FastHashCode (THREAD, JNIHandles::resolve_non_null(handle)) ;
JVM_END複製代碼
對於hashcode生成的邏輯由synchronizer.cpp
的get_next_hash
函數決定,實現比較複雜,根據hashcode的不一樣值有不一樣的生成策略,最後使用一個hash掩碼處理。ide
static inline intptr_t get_next_hash(Thread * Self, oop obj) {
intptr_t value = 0 ;
if (hashCode == 0) {
value = os::random() ;
} else
if (hashCode == 1) {
intptr_t addrBits = intptr_t(obj) >> 3 ;
value = addrBits ^ (addrBits >> 5) ^ GVars.stwRandom ;
} else
if (hashCode == 2) {
value = 1 ; // for sensitivity testing
} else
if (hashCode == 3) {
value = ++GVars.hcSequence ;
} else
if (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) ;
return value;
}複製代碼
這是一個非本地方法,判斷邏輯也十分簡單,直接==比較。函數
由本地方法表知道clone方法對應的本地函數爲JVM_Clone
,clone方法主要實現對象的克隆功能,根據該對象生成一個相同的新對象(咱們常見的類的對象的屬性若是是原始類型則會克隆值,但若是是對象則會克隆對象的地址)。Java的類要實現克隆則須要實現Cloneable接口,if (!klass->is_cloneable())
這裏會校驗是否有實現該接口。而後判斷是不是數組分兩種狀況分配內存空間,新對象爲new_obj,接着對new_obj進行copy及C++層數據結構的設置。最後再轉成jobject類型方便轉成Java層的Object類型。oop
JVM_ENTRY(jobject, JVM_Clone(JNIEnv* env, jobject handle))
JVMWrapper("JVM_Clone");
Handle obj(THREAD, JNIHandles::resolve_non_null(handle));
const KlassHandle klass (THREAD, obj->klass());
JvmtiVMObjectAllocEventCollector oam;
if (!klass->is_cloneable()) {
ResourceMark rm(THREAD);
THROW_MSG_0(vmSymbols::java_lang_CloneNotSupportedException(), klass->external_name());
}
const int size = obj->size();
oop new_obj = NULL;
if (obj->is_javaArray()) {
const int 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);
}
return JNIHandles::make_local(env, oop(new_obj));
JVM_END複製代碼
邏輯是獲取class名稱加上@再加上十六進制的hashCode。
此方法用來喚醒線程,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 EntryList
if (List == NULL) {
iterator->_next = iterator->_prev = NULL ;
_EntryList = iterator ;
} else {
List->_prev = iterator ;
iterator->_next = List ;
iterator->_prev = NULL ;
_EntryList = iterator ;
}
} else
if (Policy == 1) { // append to EntryList
if (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 ;
}
} else
if (Policy == 2) { // prepend to cxq
// prepend to cxq
if (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 ;
}
}
}
} else
if (Policy == 3) { // append to cxq
iterator->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() ;
}
}複製代碼
與notify方法相似,只是在取_WaitSet
隊列時不是取第一個而是取全部。
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複製代碼
這個方法用於當對象被回收時調用,這個由JVM支持,Object的finalize方法默認是什麼都沒有作,若是子類須要在對象被回收時執行一些邏輯處理,則能夠重寫finalize方法。
如下是廣告和相關閱讀
========廣告時間========
鄙人的新書《Tomcat內核設計剖析》已經在京東銷售了,有須要的朋友能夠到 item.jd.com/12185360.ht… 進行預約。感謝各位朋友。
=========================
相關閱讀:
談談Java基礎數據類型
從JDK源碼角度看併發鎖的優化
從JDK源碼角度看線程的阻塞和喚醒
從JDK源碼角度看併發競爭的超時
從JDK源碼角度看java併發線程的中斷
從JDK源碼角度看Java併發的公平性
從JDK源碼角度看java併發的原子性如何保證
從JDK源碼角度看Boolean