小师妹学JVM之:GC的垃圾回收算法
文章目錄
- 簡介
- 對象的生命周期
- 垃圾回收算法
- Mark and sweep
- Concurrent mark sweep (CMS)
- Serial garbage collection
- Parallel garbage collection
- G1 garbage collection
- Z Garbage Collection
- 怎么選擇
- 總結
簡介
JVM的重要性不言而喻了,如果把java的應用程序比作一輛跑車,那么JVM就是這輛車的發(fā)動機,沒有它,java程序就成了空中樓閣,無根浮萍。而在JVM中有一塊內存區(qū)域叫做運行時數(shù)據(jù)區(qū)域,存儲了運行時所需要的所有對象,而Heap Area則是其中最大的一塊。
內存畢竟不是無限的,所以就需要一種機制來將不再使用的對象進行回收,這種機制就是今天我們要講的GC。
更多精彩內容且看:
- 區(qū)塊鏈從入門到放棄系列教程-涵蓋密碼學,超級賬本,以太坊,Libra,比特幣等持續(xù)更新
- Spring Boot 2.X系列教程:七天從無到有掌握Spring Boot-持續(xù)更新
- Spring 5.X系列教程:滿足你對Spring5的一切想象-持續(xù)更新
- java程序員從小工到專家成神之路(2020版)-持續(xù)更新中,附詳細文章教程
對象的生命周期
小師妹:F師兄,你相信這個世界有輪回嗎?
師兄我是一個堅定的無神論者,活在當下就好了,何必操心后面的輪回呢?
小師妹:F師兄,這個你就不懂了,意識是組成腦的原子群的一種組合模式,我們大腦的物質基礎和一塊石頭沒有什么不同。當我們掌握大腦的組合方式,然后重構,我們的意識就重現(xiàn)了,這就是輪回。這可是量子理論中提到的觀念哦。
哇,小師妹什么時候這么厲害了,都開始探討這么高深的話題了。F師兄我實在是跟不上節(jié)奏啊。
小師妹,F師兄,我是怕你尷尬,想引出java對象的生命周期這個話題嘛。
量子理論我不熟,java對象我還沒怕過誰。
對象的生命周期其實很簡單:創(chuàng)建,使用中,最后被銷毀。
舉個最簡單的創(chuàng)建對象的例子:
Object obj = new Object();對象創(chuàng)建的時候,將會為該對象分配特定的空間。
對象創(chuàng)建之后,就可以被其他的對象使用,如果其他的對象有使用該對象,那么我們成為該對象被引用了。
當一個對象沒有被其他對象引用的時候,我們就稱為該對象可以被回收了。在Java中,對象的回收是由GC來負責的。
垃圾回收算法
小師妹:F師兄,我覺得垃圾回收好像挺簡單的,我們?yōu)槊總€對象維持一個指針計數(shù)器,每引用一次就加一,這樣不就可以實現(xiàn)垃圾回收器了嗎?
底層原理是這么一個道理,但是JVM需要一種更加高效的算法來保證垃圾回收的效率,同時也不會影響正在運行的程序。
接下來我們將會介紹一下,在JVM中比較常用幾個垃圾回收算法:
Mark and sweep
Mark and sweep是最最簡單的垃圾回收算法,簡單點講,它可以分為兩個步驟:
標記live對象聽起來很簡單,就是掃描堆中的對象,看這些對象是否被引入。
但是這里有一個問題,如果是兩個對象互相引用的時候,而這兩個對象實際上并沒有被外部的對象所引用,那么這兩個對象其實是應該被回收的。所以我們還需要解決一個關鍵性的問題:從哪里開始掃描的問題。
JVM定義了一些Root對象,從這些對象開始,找出他們引用的對象,組成一個對象圖。所有在這個圖里面的對象都是有效的對象,反之不在對象圖中的對象就應該被回收。有效的對象將會被Mark為alive。
這些Root對象包括:正在執(zhí)行的方法中的本地對象和輸入?yún)?shù)。活動的線程,加載類中的static字段和JNI引用。
注意,這種遍歷其實是有個缺點的,因為為了找到對象圖中哪些對象是live的,必須暫停整個應用程序,讓對象變成靜止狀態(tài),這樣才能構建有效的對象圖。后面我們會介紹更加有效的垃圾回收算法。
掃描對象之后,我們就可以將未標記的對象刪除了。
刪除有三種方式,第一種方式是正常刪除。但是正常刪除會導致內存碎片的產生。所以第二種方式就是刪除之后進行壓縮,以減少內存碎片。還有一種方式叫做刪除拷貝,也就是說將alive的對象拷貝到新的內存區(qū)域,這樣同樣可以解決內存碎片的問題。
Concurrent mark sweep (CMS)
在講CMS之前,我們先講一下垃圾回收器中的Eden,Old和Survivor space幾個大家應該都很熟悉的分代技術。
Young Gen被劃分為1個Eden Space和2個Suvivor Space。當對象剛剛被創(chuàng)建的時候,是放在Eden space。垃圾回收的時候,會掃描Eden Space和一個Suvivor Space。如果在垃圾回收的時候發(fā)現(xiàn)Eden Space中的對象仍然有效,則會將其復制到另外一個Suvivor Space。
就這樣不斷的掃描,最后經過多次掃描發(fā)現(xiàn)任然有效的對象會被放入Old Gen表示其生命周期比較長,可以減少垃圾回收時間。
之后要將的幾個垃圾回收器,除了ZGC,其他都使用的是分代的技術。
好了,現(xiàn)在繼續(xù)講CMS,CMS是mark and swap的升級版本,它使用多個線程來對heap區(qū)域進行掃描,從而提升效率。
CMS在Young Generation中使用的是mark-copy,而在Old Generation主要使用的是mark-sweep。
使用CMS的命令很簡單:
-XX:+UseConcMarkSweepGC上面是列出的一些CMS的調優(yōu)參數(shù)。
Serial garbage collection
Serial garbage collection使用單一的線程來進行垃圾回收操作,其好處就是不需要和其他的線程進行交互。如果你是單核的CPU,那么最好就是選擇Serial garbage collection,因為你不能充分利用多核的好處。同樣的它也常常用在比較小型的項目中。
Serial garbage collection在Young Generation中使用的是mark-copy,而在Old Generation主要使用的是 mark-sweep-compact。
下面是開啟命令:
-XX:+UseSerialGCParallel garbage collection
和serial GC類似,它在Young Generation中使用的是mark-copy,而在Old Generation主要使用的是 mark-sweep-compact。不同的是它是并行的。
可以通過下面的命令來指定并發(fā)的線程:
-XX:ParallelGCThreads=N如果你是多核處理器,那么Parallel GC可能是你的選擇。
Parallel GC是JDK8中的默認GC。而在JDK9之后, G1是默認的GC。
使用下面的命令來開啟Parallel GC:
-XX:+UseParallelGCG1 garbage collection
為什么叫G1呢,G1=Garbage First,它是為替換CMS而生的,最早出現(xiàn)在java7中。
G1將heap區(qū)域劃分成為多個更小的區(qū)域,每個小區(qū)域都被標記成為young generation 或者old generation。從而運行GC在更小的范圍里運行,而不是影響整個heap區(qū)域。
可以使用下面的命令來開啟:
-XX:+UseG1GCZ Garbage Collection
ZGC是一個可擴展的,低延遲的GC。ZGC是并發(fā)的,而且不需要停止正在運行的線程。
使用下面的命令來開啟:
-XX:+UseZGCZGC是在JDK11中被引入的。
怎么選擇
小師妹:F師兄,你講了這么多個GC,到底我該用哪個呢?
高射炮不能用來打蚊子,所以選擇合適的GC才是最終要的。這里F師兄給你幾個建議:
如果你的應用程序內存本來就很小,那么使用serial collector : -XX:+UseSerialGC.
如果你的程序運行在單核的CPU上,并且也沒有程序暫停時間的限制,那么還是使用serial collector : -XX:+UseSerialGC.
如果對峰值期的性能要求比較高,但是對程序暫停時間沒多大的要求,那么可以使用 parallel collector: -XX:+UseParallelGC。
如果更加關注響應時間,并且GC的對程序的暫停時間必須要小,那么可以使用-XX:+UseG1GC。
如果響應時間非常重要,并且你在使用大容量的heap空間,那么可以考慮使用ZGC: -XX:UseZGC。
總結
本文介紹了幾種GC的算法,大家可以根據(jù)需要選用。
本文作者:flydean程序那些事
本文鏈接:http://www.flydean.com/jvm-gc-algorithms/
本文來源:flydean的博客
歡迎關注我的公眾號:程序那些事,更多精彩等著您!
總結
以上是生活随笔為你收集整理的小师妹学JVM之:GC的垃圾回收算法的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 小师妹学JVM之:JVM的架构和执行过程
- 下一篇: 小师妹学JVM之:深入理解JIT和编译优