Java後端技術 2017-03-22 23:52
一、對象的建立
建立對象是通過new關鍵字來實現,對於JVM來說new關鍵字背後還有很多細節。當建立一個對象,例如:Object object = new Object(),它對應的位元組碼指令是new(對象數組和基本類型數組不是),當執行到new指令時,虛擬機器會先檢查對應的類是否被載入過,如果沒有被載入,那麼執行類載入的過程。載入完畢後就需要為對象分配記憶體空間,分配演算法主要分兩種,一是指標碰撞,如果堆記憶體是整齊的,使用的記憶體和未使用的記憶體以分界點分開,那麼為新生對象分配記憶體就是將分界點挪動對象同等大小的位置就可以了。翻看JVM源碼可以知道,虛擬機器採用了CAS和失敗重試的方式保證記憶體配置的安全執行緒。如果堆記憶體是不整齊的,使用的記憶體和未使用的記憶體沒有明確的分界點,零零散散的,那麼虛擬機器必須得記錄哪些記憶體是正在使用的,哪些記憶體是未使用的,分配記憶體的時候需要找一塊大於或者等於當前對象大小的記憶體空間,這種分配演算法稱為空白閑列表。虛擬機器採用什麼方式為對象分配記憶體需要看使用的垃圾收集器是否帶有整理功能。記憶體配置好了以後,就需要進行初始化操作,比如將空間初始化為零值,調用建構函式等等。
二、哪些對象需要被GC回收
判定一個對象需要回收主要有以下兩種方法:
1、引用計數法,當有一個地方引用對象時,計數器加1,不引用時就減1。任何時刻,當對象的引用計數器為0時,表示著該對象已經沒有被引用了,所以可以回收此類對象。這種演算法最大的缺點就是解決不了相互引用的問題,比方說:A類中引用了B類執行個體,B類中引用了A類執行個體,但是A和B都沒有被任何地方引用,此時A和B的引用計數都不為0,那麼垃圾收集就無法回收此類對象而導致記憶體流失,有興趣的讀者可能不相信想寫個例子試一試,結論是可以被回收的,因為Java虛擬機器並沒有採用這種演算法。
2、可達性分析演算法,這種演算法是以一些稱之為GC ROOTS的對象作為起始點,從起點一直往下枚舉,如果GC ROOTS到對象不可達,也就是說從GC ROOTS開始,沒有任何一條路徑能夠達到記憶體中的對象,那麼該對象就已經脫離“組織”了,垃圾收集器就可以回收該對象。作為GC ROOTS的對象有:虛擬機器棧中引用的對象、類靜態屬性引用的對象、常量引用的對象、native方法中引用的對象。
三、對象的finalize方法
當GC ROOTS到對象不可達時,那麼該對象會被進行一次標記和篩選,如果對象實現了finalize方法,那麼對象會被放到一個ReferenceQueue隊列當中,這個隊列會被虛擬機器啟動的一個叫做Finalizer的後台線程死迴圈調用queue.remove(),這個方法會一直阻塞,直到有元素返回,返回的元素將會調用其finalize方法。很多資料上說Finalizer線程優先順序很低,其實它的優先順序並不低:Thread.MAX_PRIORITY - 2 = 8,而Thread的預設優先順序為5。通過看原始碼可以知道,通過jstack出來的線程堆棧可以看到這個線程的優先順序是8。
想要知道更多底層的細節並不是一件容易的事情,需要查閱很多資料。雖然可以通過看openJDK的原始碼瞭解更多,但是相信很多朋友對openJDK的原始碼還是一頭霧水,不知道從哪裡看起。有好方法的朋友歡迎留言一起討論,一起學習,一起進步。