Object類
Object類是類階層的根類。Object類是每一個類的超類。所有對象,包括數組,都實現了這個類的方法。
Object類屬於java.lang包,所有類都直接或間接繼承Object類,在Jdk1.6版本中Object類共有11個方法。
Object類中有很多native方法,也稱為本地方法,具體是用C(C++)在動態庫中實現的,然後通過JNI調用。 源碼分析
package java.lang;public class Object { private static native void registerNatives(); static { registerNatives(); // 本地方法,對象初始化時自動調用此方法 } // 1. 返回這個Object的運行時Class對象。此對象也被用於該類的static synchronized方法的監視器對象。 public final native Class<?> getClass(); // 2. 返回這個對象的雜湊值,預設是返回該對象的記憶體位址。重寫此方法可以提高雜湊結構的集合的效能,例如HashMap。 public native int hashCode(); // 3. 對象比較方法,判斷當前對象與其它對象是否相等,預設是比較兩個對象的地址是否相同。 // 一般重寫equals方法時會同時重寫hashCode方法 public boolean equals(Object obj) { return (this == obj); } // 4.對象複製方法用於對象的複製,支援複製的類必須要實現Cloneable介面,如果沒有實現Cloneable介面,執行clone方法會產生異常。 protected native Object clone() throws CloneNotSupportedException; // 5. 返回該對象的字串表示形式,預設是使用類名+16進位的hashcode值(記憶體位址),重寫此方法可以提高類的可讀性。 public String toString() { return getClass().getName() + "@" + Integer.toHexString(hashCode()); } // 6. 線程喚醒(單個),喚醒在此對象監視器上等待的單個線程。可用於線程間相互協作通訊。 public final native void notify(); // 7. 線程喚醒(所有),喚醒在此對象監視器上等待的所有線程。 public final native void notifyAll(); // 8. 線程等待,本地方法。timeout表示要等待的最長時間(以毫秒為單位)。 public final native void wait(long timeout) throws InterruptedException; // 9. 線程等待,支援毫微秒修正值,進行四捨五入 public final void wait(long timeout, int nanos) throws InterruptedException { //... nanos為額外時間(單位為毫微秒),取值範圍是0~999999,當nanos>=500000,則timeout值加1 wait(timeout); } // 10. 線程等待,與notify方法是相對的方法,wait與nofity兩個方法實現線程通訊,這兩個方法必須在同一個監控器對象的同步代碼塊中使用。 public final void wait() throws InterruptedException { wait(0); } // 11. 類似解構函式,當記憶體回收行程確定不存在對該對象的更多引用時,由對象的記憶體回收行程調用此方法,用於釋放資源。 // finalized方法在由GC調用,並且還允許一次再生(標記為finalized後該對象在這個線程中又可達了),可能會拖慢GC速度導致頻繁fullGC,甚至直接OOM。 // finalize的執行是不確定的,既不確定由哪個線程執行,也不確定執行的順序。 // 由於finalize方法可能影響GC,因此可以使用PhantomReference虛引用來替代finalize的功能。 // effective java告訴我們,最好的做法是提供close()方法,並且告知上層應用在不需要該對象時一掉要調用這類介面。 protected void finalize() throws Throwable { }}finalize方法缺陷 重寫finalize方法對象的建立過程
建立對象A執行個體 建立java.lang.ref.Finalizer對象執行個體F1,F1指向A和一個ReferenceQueue,參考關聯性:F1—>A,F1—>ReferenceQueue(Finalizer類靜態方法建立一個後台線程從queue中執行runFinalizer方法) java.lang.ref.Finalizer的類對象引用F1,這樣可以保持F1永遠不會被回收,除非解除Finalizer的類對象對F1的引用。 經過上述三個步驟,參考關聯性:java.lang.ref.Finalizer–>F1–>A,F1–>ReferenceQueue。 重寫finalize方法對象的GC回收過程
在發生minor gc時,即便一個對象A不被任何其他對象引用,只要它含有override finalize(),就會最終被java.lang.ref.Finalizer類的一個對象F1引用 如果新生代的對象都含有override finalize(),那豈不是無法GC。沒錯,這就是finalize()的第一個風險所在; minor gc會把所有活躍對象以及被java.lang.ref.Finalizer類對象引用的(實際)垃圾對象拷貝到下一個survivor地區,如果拷貝溢 出,就將溢出的資料晉陞到老年代,極端情況下,老年代的容量會被迅速填滿,於是讓人頭痛的頻繁full gc就離我們不遠了。 當第一次minor gc中發現一個對象只被java.lang.ref.Finalizer類對象引用時,GC線程會把指向對象A的Finalizer對象F1塞入F1所引用的ReferenceQueue中; java.lang.ref.Finalizer類對象中包含了一個運行層級很低的deamon線程 finalizer來非同步地調用這些對象的finalize()方法,調用完之後,java.lang.ref.Finalizer類對象會清除自己對 F1的引用。這樣GC線程就可以在下一次minor gc時將對象A回收掉。 可見Finalizer對象的多少也會直接影響minor gc的快慢。 finalizer方法的對象回收過程總結下來,有以下三個風險:
如果隨便一個finalize()拋出一個異常,finallize線程會終止,很快地會由於ReferenceQueue的不斷增長導致OOM finalizer線程運行層級很低,有可能出現finalize速度跟不上對象建立速度,最終可能還是會OOM,實際應用中一般會有富裕的CPU時間,所以這種OOM情況可能不太常出現 含有override finalize()的對象至少要經曆兩次GC才能被回收,嚴重拖慢GC速度,運氣不好的話直接晉陞到老年代,可能會造成頻繁的full gc,進而影響這個系統的效能和吞吐率。
參考資料
* Java Object類源碼閱讀
* java中Object類 原始碼詳解
* 深入理解java的finalize
* finalize實現原理與由此引發的血案
* Java的Finalizer引發的記憶體溢出