JVM培訓之一些GC演算法的理論知識

dadapeng發表於2018-09-16

很精彩的一次內部分享,介紹了大部分的GC演算法理論知識,JVM博大精深,本篇文章只是結合本次內部分享總結的一些理論知識,如果有大佬有疑問,歡迎留言指出!

Concurrent:併發,程式一邊執行一邊做GC

Parallel:並行,一塊區域,一個人做清掃,需要100s,但是把區域分成兩塊,用兩個人掃,時間就縮短了一半;所以GC演算法有並行的,有不是並行的,這些都是可以根據自己的實際業務去選擇的,只有最適合自己的GC演算法;

Stop The World:簡稱STW,某些GC演算法當中,當需要做GC的時候,程式是必須停下來的,程式就是一個終止狀態,當GC結束之後再恢復;Java的GC很多時候都是STW,舉一個簡單的栗子,一邊掃地一邊嗑瓜子,這樣的話是永遠掃不乾淨的,記憶體的使用率就不是很高,當然STW有一個很大的缺點,就是程式必須終止,如果說做一次GC用時太長,需要十幾秒或者一分鐘或更多,那肯定是無法忍受的,這就需要GC調優來完成,通過演算法把時間控制在最短,讓使用者無感知;

root set:公集合,GC的時候會確定現在哪些物件還是存活的,

Generation GC:分代GC,先說一代GC,比方說現在有一個2G的記憶體區域,那麼需要GC的時候,需要把整塊記憶體區域2G遍歷一次,來做GC,這是一個很複雜的過程,需要判斷這麼多的物件是否引用或者被引用,可想而知耗時肯定會很長;以老王的經驗,1G記憶體做一次full GC停頓時間在1-3s,100G記憶體做一次full GC,停頓時間在1-3min,也就是說這幾分鐘程式什麼都幹不了,肯定是無法接受的;所以產出了分代GC,把一個區域分成幾個部分,年輕代1,年輕代2,和年老代。比方說程式開始執行,新產生的物件放在年輕代1,裝滿之後,結合copy演算法,從root節點出發,可能真正存活的物件只有很少一部分,那麼我們只把這部分物件拷貝到年輕代2裡面,然後將年輕代1區域全部清掃掉,這個copy演算法非常高效,但前提是需要程式的物件大部分都是用完一次就可以扔掉的,不然的話,存活的物件太多了,copy演算法就變得很低效了;所以copy演算法的效率取決於剩餘的物件,剩餘的物件越多,效率越低,剩餘的物件越少,效率越高;

RefCount:引用計數演算法,當有指標指向這個物件的時候,就把計數器+1,當不指向這個物件的時候把計數器-1,當這個物件的計數器為0的時候,這個物件的記憶體就可以釋放掉了,Python就是用的這個演算法,官方的python流派就是用的這個演算法,微軟的com組建結構也是用的這個引用計數演算法;這個演算法兩個缺點:效率低,因為訪問物件是一個很頻繁的操作,這樣頻繁做++;掃不乾淨,迴圈列表,a->b->c,其實外部沒有引用,但是內部有互相引用,其實abc都是垃圾;

MarkSweep:標記清除演算法,有很多物件,從公集合出發找其他的物件,是活的物件就做標記,標記的過程中,程式是不能跑的,當標記完了之後,沒有被標記的物件,就都可以釋放掉了,但是清掃之後的記憶體區域會變得千瘡百孔,再分配記憶體的時候,需要在這些記憶體中,找一個合適自己的記憶體區域;

MarkCompact:標記壓縮演算法,可以理解是標記清除演算法的一個升級版,同樣是在活物件上標記,但是它會選一個活物件作為一個端,把另外標記的活物件往這一端擠,這樣被釋放的區域就會是一大塊的;

Safepoint:當程式碼執行到某個點,這個點可以做GC,這個點就叫safepoint,做GC的時候,所有的執行緒都需要停下來,就是所有的執行緒需要跑到這個安全點,跑到這個點的時候,Java虛擬機器會生成一條指令,這個指令會讓這個執行緒訪問一個記憶體地址,這也是為了快速響應;因為執行緒停下來做別的事情非常耗效能;

Reference:強引用,強引用是我只要引用你,你就不會被垃圾回收;當然還有WeakReference弱引用和軟引用SoftReference等;

Java虛擬機器的幾個GC演算法:

  年輕代:Serial:序列,STW特性

      ParNew:多執行緒,也是STW

      ParScavenge:多執行緒,同樣是STW,是ParNew升級版

 

  年老代:CMS:試圖平衡停頓時間,這個演算法的過程為:InitialMark->CCMark->Remark->CCSweep

      SerialOld:MarkCompact演算法上面提到過的

      ParOld:同樣是MarkCompact演算法,是SerialOld升級版

年輕代和年老代GC演算法是可以組合的,除了部分;

ZGC:Java11版本釋出推出,管理TB級記憶體,GC時間控制在10ms;

 

相關文章