GC是什么時候執行的
垃圾回收是一種回收無用內存空間並使其對未來實例可用的過程。
Eden 區:當一個實例被創建了,首先會被存儲在堆內存年輕代的 Eden 區中。
Survivor 區(S0 和 S1):作為年輕代 GC(Minor GC)周期的一部分,存活的對象(仍然被引用的)從 Eden 區被移動到 Survivor 區的 S0 中。類似的,垃圾回收器會掃描 S0 然后將存活的實例移動到 S1 中。
(譯注:此處不應該是Eden和S0中存活的都移到S1么,為什么會先移到S0再從S0移到S1?)
死亡的實例(不再被引用)被標記為垃圾回收。根據垃圾回收器選擇的不同,要么被標記的實例都會不停地從內存中移除,要么回收過程會在一個單獨的進程中完成。
老年代: 老年代(Old or tenured generation)是堆內存中的第二塊邏輯區。當垃圾回收器執行 Minor GC 周期時,在 S1 Survivor 區中的存活實例將會被晉升到老年代,而未被引用的對象被標記為回收。
老年代 GC(Major GC):相對於 Java 垃圾回收過程,老年代是實例生命周期的最后階段。Major GC 掃描老年代的垃圾回收過程。如果實例不再被引用,那么它們會被標記為回收,否則它們會繼續留在老年代中。
內存碎片:一旦實例從堆內存中被刪除,其位置就會變空並且可用於未來實例的分配。這些空出的空間將會使整個內存區域碎片化。為了實例的快速分配,需要進行碎片整理。基於垃圾回收器的不同選擇,回收的內存區域要么被不停地被整理,要么在一個單獨的GC進程中完成。
垃圾回收中實例的終結
在釋放一個實例和回收內存空間之前,Java 垃圾回收器會調用實例各自的 finalize() 方法,從而該實例有機會釋放所持有的資源。雖然可以保證 finalize() 會在回收內存空間之前被調用,但是沒有指定的順序和時間。多個實例間的順序是無法被預知,甚至可能會並行發生。程序不應該預先調整實例之間的順序並使用 finalize() 方法回收資源。
- 任何在 finalize過程中未被捕獲的異常會自動被忽略,然后該實例的 finalize 過程被取消。
- JVM 規范中並沒有討論關於弱引用的垃圾回收機制,也沒有很明確的要求。具體的實現都由實現方決定。
- 垃圾回收是由一個守護線程完成的。
如何判斷對象是否可以被回收
jvm 虛擬機會通過 可達性分析法,從gc root開始向下搜索,被搜索走過的路徑稱為引用鏈,當一個對象沒有被GC roots引用鏈連接時,則證明此對象不可用,那么虛擬機就判斷這個對象是可以被回收的。
GC roots 的對象有哪些
1 虛擬機棧(棧幀中的本地變量表)中引用的對象
2 方法區中類靜態屬性引用的對象
3 方法區中常量引用的對象
4 本地方法棧中Native方法引用的對象
可達性算法中的不可達對象並不是立即死亡的,對象擁有一次自我拯救的機會。對象被系統宣告死亡至少要經歷兩次標記過程,第一次是經過可達性分析發現沒有與GC roots 相連接的引用鏈,第二次是由虛擬機自動建立的Finallizer隊列中判斷是否需要執行finalize()方法。
當對象變成(GC roots)不可達是,GC會判斷該對象是否覆蓋了 finalize方法,若未覆蓋,則直接將其回收,若對象為執行finalize方法,將其放入F-Queue隊列,由一低優先級線程執行該隊列中對象的finalize方法。執行finalize方法完畢后,GC會再次判斷該對象是否可達,若不可達,則進行回收,否則對象”復活“。
每個對象只能觸發異常finalize()方法
由於finalize()方法運行代價高昂,不確定性大,無法保證各個對象的調用順序,不推薦使用。