一文讀懂Java七大垃圾收集器
java和c++最大的不同就是java中不需要手動去回收物件,在執行時jvm會自動幫我們進行垃圾回收,接下來讓我們揭開垃圾收集器的面紗
名詞解釋
並行:多條執行緒並行工作
併發:多個執行緒併發執行,但他們不是並行的,有可能交交替執行
吞吐量:吞吐量=執行使用者程式碼時間/(執行使用者程式碼時間+垃圾收集時間))
為什麼要垃圾回收?
不回收你養我啊,就和我們電腦磁碟滿了刪除檔案、房間亂了整理房間一個道理
什麼時候回收垃圾?
- 老年代或年輕代物件滿,無法存放新物件時觸發GC
- 程式碼中呼叫System.gc(),觸發Full GC,可能會連帶Minor GC
- 程式執行的時候有一條低優先順序的GC執行緒,它是一條守護執行緒,當這條執行緒處於執行狀態的時候,自然就觸發了一次GC了
垃圾收集器概覽
圖中可以看到,新生代中有三個垃圾回收器,老年代中有三個回收器,而G1既可以在新生代使用也可以在老年代使用。連線線代表可以組合使用的收集器。
各種垃圾收集器的優缺點
新生代垃圾收集器:Serial
新生代垃圾收集器:ParNew
新生代垃圾收集器:Parallel Scavenge
老年代垃圾收集器:Serial Old
老年代垃圾收集器:Parallel Old
老年代垃圾收集器:CMS
執行過程:
老少通吃垃圾收集器:G1
執行過程:
為什麼G1收集器可以實現可預測的停頓?
- G1 收集器避免全區域垃圾收集,它把堆記憶體劃分為大小固定的幾個獨立區域
- 跟蹤這些區域的垃圾收集進度,同時在後臺維護一個優先順序列表
- 每次根據所允許的收集時間, 優先回收垃圾最多的區域**(名稱Garbage-First的由來)**
- 區域劃分和優先順序區域回收機制,確保 G1 收集器可以在有限時間獲得最高的垃圾收
集效率
總結:收集器雖然有七個,單理解了java的發展歷史之後也就不足為奇,比如最早的單核機器執行,就有Serial,後來有多核cpu,就有了ParNew(名字不好理解),再後來既要多執行緒又要停頓,就有了Parallel Scavenge,高吞吐量+高效的利用cpu時間
再再再後來,就是G1,可以獨立使用,不僅老少通吃,還能不犧牲吞吐量情況下控制垃圾回收所造成的停頓時間且不產生記憶體碎片,目前這也是垃圾收集器理論發展的最前沿成果
垃圾收集演算法
複製演算法(Coping)
目前java1.8中年輕代使用的就是複製演算法,因為大部分物件都是可被回收的
標記清除演算法(Mark-Sweep)
如圖可知(四個字值1分了( ﹁ ﹁ ) ~→),僅僅是把一些可以回收的物件及逆行了回收,其它存活物件和未使用的區域不做任何處理
不足:存活物件七零八落,碎片化嚴重,當有物件需要申請連續空間時無法申請到
效率問題
標記整理演算法(Mark-Compact)
看名字就容易猜到,先標記出可回收物件,然後再對存活的物件進行整理,可以解決複製演算法的記憶體問題也可以解決標記清除演算法的碎片問題,簡直完美(完美表情)
分代收集演算法
不是一種具體的演算法,僅僅是對演算法的組合,通過對不同的區域使用不同的演算法
- 標記整理演算法-》老生代的特點是每次垃圾回收時只有少量物件需要被回收
- 複製演算法-》新生代的特點是每次垃圾回收時都有大量垃圾需要被回收
無論是哪一種演算法,都有它的使用場景,比如物件複製演算法適合很多物件可回收的情況;標記整理演算法適合沒有很多記憶體的情況
如何判定物件是否可回收(是垃圾)
總結
收集器那麼多,那我該用哪個?
當然,在JDK 11當中,加入了實驗性質的ZGC。它的回收耗時平均不到2毫秒。它是一款低停頓高併發的收集器,但並沒有實際的接觸,我們用的都是JDK 1.8