常見的 GC 演演算法。參照計數法、複製演演算法、標記-清除法、標記整理法、三色標記法、分代收集法。
這哥仨是JVM的,今天不是主角,就簡單說說
分代收集是傳統 Mark-Sweep 的一個改進。這個演演算法是基於一個經驗:絕大多數物件的生命週期都很短。所以按照物件的生命週期長短來進行分代。
一般 GC 都會分三代,在 java 中稱之為新生代(Young Generation)、年老代(Tenured Generation)和永久代(Permanent Generation);在 .NET 中稱之為第 0 代、第 1 代和第2代。
原理如下:
新物件放入第 0 代
當記憶體用量超過一個較小的閾值時,觸發 0 代收集
第 0 代倖存的物件(未被收集)放入第 1 代
只有當記憶體用量超過一個較高的閾值時,才會觸發 1 代收集
2 代同理
因為 0 代中的物件十分少,所以每次收集時遍歷都會非常快(比 1 代收集快幾個數量級)。只有記憶體消耗過於大的時候才會觸發較慢的 1 代和 2 代收集。
因此,分代收集是目前比較好的垃圾回收方式。使用的語言(平臺)有 jvm、.NET 。
三色標記法是傳統 Mark-Sweep 的一個改進,是一個並行的 GC 演演算法。
首先做個規定:
有了這個,我們來看演演算法:
像CMS和G1都是用到了這個,只是做了億點點改進,而且三色標記會產生浮動垃圾,這是不可避免的,而且CMS就是在三色標記這塊有個大bug導致STW有時候巨長,所以JDK沒用他做預設垃圾回收器,但是CMS仍然是一個承上啟下的開創性的並行垃圾回收器。
GO的GC是並行GC, 也就是GC的大部分處理和普通的go程式碼是同時執行的, 這讓GO的GC流程比較複雜.
目前整個GC流程會進行兩次STW(Stop The World), 第一次是Stack scan階段, 第二次是Mark Termination階段.
第一次STW會準備根物件的掃描, 啟動寫屏障(Write Barrier)和輔助GC(mutator assist).
第二次STW會重新掃描部分根物件, 禁用寫屏障(Write Barrier)和輔助GC(mutator assist).
從1.8以後的golang將第一步的stop the world 也取消了,這又是一次優化;
1.9開始, 寫屏障的實現使用了Hybrid Write Barrier, 大幅減少了第二次STW的時間.
主動的話,通過呼叫 runtime.GC(),這是阻塞式的。
自動垃圾回收的觸發條件有兩個:
閾值是由一個gcpercent的變數控制的,當新分配的記憶體佔已在使用中的記憶體的比例超過gcprecent時就會觸發。
比如一次回收完畢後,記憶體的使用量為5M,那麼下次回收的時機則是記憶體分配達到10M的時候。也就是說,並不是記憶體分配越多,垃圾回收頻率越高。
如果一直達不到記憶體大小的閾值呢?這個時候GC就會被定時時間觸發,比如一直達不到10M,那就定時(預設2min觸發一次)觸發一次GC保證資源的回收。