在堆裏面存放着Java世界中幾乎全部的對象實例,垃圾收集器在對堆進行回收前,第一件事情就是要肯定這些對象哪些應該被回收。java
在對象中添加一個引用計數器,每當有一個地方引用它時,計數器值就加一;當引用失效時,計數器值就減一;任什麼時候刻計數器爲零的對象就是不可能再被使用的。web
存在的問題:很難解決對象之間循環引用的問題。算法
主流的Java虛擬機都沒有選擇該方式來管理內存。緩存
基本思路:經過一系列稱爲「GC Roots」的根對象做爲起始節點集,從這些節點開始,根據引用關係向下搜索,搜索過 程所走過的路徑稱爲「引用鏈」,若是某個對象到GC Roots間沒有任何引用鏈相連, 或者用圖論的話來講就是從GC Roots到這個對象不可達時,則證實此對象是不可能再被使用的。以下圖多線程
圖片來源:《深刻理解Java虛擬機》併發
固定可做爲GC Roots的對象包括如下幾種:編輯器
在JDK 1.2版以後,Java對引用的概念進行了擴充,將引用分爲強引用(Strongly Re-ference)、軟 引用(Soft Reference)、弱引用(Weak Reference)和虛引用(Phantom Reference)4種,這4種引用強 度依次逐漸減弱。ide
若是對象在進行可達性分析後發現沒有與GC Roots相鏈接的引用鏈,那它將會被第一次標記,隨後進行一次篩選,篩選的條件是此對象是否有必要執行finalize()方法。假如對象沒有覆蓋finalize()方法,或者finalize()方法已經被虛擬機調用過,那麼虛擬機將這兩種狀況都視爲「沒有必要執行」。post
對象能夠在finalize()方法中經過將本身與引用鏈上的對象從新關聯,來逃脫垃圾回收。測試
PS:至今沒寫過這個方法。不推薦使用。
/** * 此代碼演示了兩點: * 1.對象能夠在被GC時自我拯救。 * 2.這種自救的機會只有一次,由於一個對象的finalize()方法最多隻會被系統自動調用一次 * * 代碼來源:《深刻理解Java虛擬機》 * @author : fuyuaaa * @date : 2020-06-03 15:58 */ public class FinalizeEscapeGC { public static FinalizeEscapeGC SAVE_HOOK = null; public void isAlive() { System.out.println("yes, i am still alive :)"); } @Override protected void finalize() throws Throwable { super.finalize(); System.out.println("finalize method executed!"); FinalizeEscapeGC.SAVE_HOOK = this; } public static void main(String[] args) throws Throwable { SAVE_HOOK = new FinalizeEscapeGC(); //對象第一次成功拯救本身 SAVE_HOOK = null; System.gc(); // 由於Finalizer方法優先級很低,暫停0.5秒,以等待它 Thread.sleep(500); if (SAVE_HOOK != null) { SAVE_HOOK.isAlive(); } else { System.out.println("no, i am dead :("); } // 下面這段代碼與上面的徹底相同,可是此次自救卻失敗了,由於finalize只會被執行一次 SAVE_HOOK = null; System.gc(); // 由於Finalizer方法優先級很低,暫停0.5秒,以等待它 Thread.sleep(500); if (SAVE_HOOK != null) { SAVE_HOOK.isAlive(); } else { System.out.println("no, i am dead :("); } } } result: finalize method executed! yes, i am still alive :) no, i am dead :( 複製代碼
方法區的垃圾收集主要回收兩部份內容:
廢棄的常量:沒有地方引用這個常量
再也不使用的類型:(同時知足三個條件)
首先標記出全部須要回收的對象,在標記完成後,統一回收掉全部被標記的對象;也能夠反過來,標記存活的對象,統一回收全部未被標記的對象。
缺點:
將可用內存按容量劃分爲大小相等的兩塊,每次只使用其中的一塊。當這一塊的內存用完了,就將還存活着的對象複製到另一塊上面,而後再把已使用過的內存空間一次清理掉。
缺點:
如今的商用Java虛擬機大多都優先採用了這種收集算法去回收新生代。
標記過程仍然與「標記-清除」算法同樣,但後續步驟不是直接對可回收對象進行清理,而是讓全部存活的對象都向內存空間一端移動,而後直接清理掉邊界之外的內存。
缺點:
PS:
上圖爲HotSpot虛擬機的。有連線表明能夠搭配使用。
因爲維護和兼容性測試的成本,在JDK 8時將Serial+CMS、 ParNew+Serial Old這兩個組合聲明爲廢棄(JEP 173),並在JDK 9中徹底取消了這些組合的支持(JEP 214)。
並行(Parallel):並行描述的是多條垃圾收集器線程之間的關係,說明同一時間有多條這樣的線程在協同工做,一般默認此時用戶線程是處於等待狀態。
併發(Concurrent):併發描述的是垃圾收集器線程與用戶線程之間的關係,說明同一時間垃圾收集器線程與用戶線程都在運行。因爲用戶線程並未被凍結,因此程序仍然能響應服務請求,但因爲垃圾收集器線程佔用了一部分系統資源,此時應用程序的處理的吞吐量將受到必定影響。
具體步驟:
G1把連續的Java堆劃分爲多個大小相等的獨立區域(Region),每個Region均可以扮演新生代的Eden空間、Survivor空間,或者老年代空間。收集器可以對扮演不一樣角色的 Region採用不一樣的策略去處理。
Region中還有一類特殊的Humongous區域,專門用來存儲大對象。G1認爲只要大小超過了一個 Region容量一半的對象便可斷定爲大對象。
可預測的停頓時間模型:將Region做爲單次回收的最小單元,讓G1收集器去跟蹤各個Region裏面的垃圾堆積的「價值」大小,價值即回收所得到的空間大小以及回收所需時間的經驗值,而後在後臺維護一個優先級列表,每次根據用戶設定容許的收集停頓時間,優先處理回收價值收益最大的那些Region,這也就是「Garbage First」名字的由來。
具體步驟:
PS:
圖片來源《深刻理解Java虛擬機》
本文使用 mdnice 排版