一分鐘系列:go語言運行時-垃圾回收

在介紹垃圾回收之前,我們得先知道垃圾回收的作用是什麼?

垃圾回收能讓讓我們省去自己釋放內存的煩惱,go語言的運行時會幫我們自動去釋放不用了的內存。

在介紹go垃圾回收原理之前,我們先了解下常見的垃圾回收方法。

引用計數

引用計數是對我們使用的每塊內存都做一個計數器,當計數器為0的時候,就釋放這塊內存。

優點

  • 簡單清晰,效率高,因為每個對象都有自己的計數器。

缺點

  • 容易內存洩漏:不好維護計數器
  • 不支持循環引用

代齡

根據對象存活時間,分為不同的組別。

優點

  • 有效減少每次gc時掃描的對象:每次都將存活時間長的對象放入老生代
  • 內存壓縮:發生代齡移動的時候,可以做內存的壓縮,減少碎片,提高利用率

標記清理

go語言使用的是3色標記,釋義如下:

一分鐘系列:go語言運行時-垃圾回收

1. 有黑白灰三個集合. 初始時所有對象都是白色

2. 從Root對象開始標記, 將所有可達對象標記為灰色

3. 從灰色對象集合取出對象, 將其引用的對象標記為 灰色, 放入灰色集合, 並將自己標記為黑色

4. 重複第三步, 直到灰色集合為空, 即所有可達對象都 被標記

5. 標記結束後, 不可達的白色對象即為垃圾. 對內存進 行迭代清掃, 回收白色對象.

6. 重置GC狀態


當我們進行標記的時候,如果程序還在不斷的申請、釋放指針,會導致已經掃描的數據不正確的,這個時候粗暴的做法就是STW(stop the world)?

怎麼解決?如何介紹stw的時間?

將垃圾回收的過程分為:掃描和清理兩個階段。

如果我們掃描階段是stw,那清理階段我們是可以和用戶邏輯是並行的,因為這個時候,白色的對象已經確認不會被引用了。

所以清理階段是可以並行的,那標記階段,如果不stw可能帶來的問題是:

一分鐘系列:go語言運行時-垃圾回收

可以看到,此時當開始掃描的時候,A被標記為黑色,然後在A標記為黑色,B還麼掃描之前,將A指向了C,此時掃描B的時候就無法找到C了,導致C被誤回收,那這個怎麼解決呢?

我們希望在stw期間,如果已經標記為黑色的對象發生了引用的改變,我們將其重新標記為灰色,然後進行掃描,就能有效解決問題,這個技術在go中就是“寫屏障”。

總結

以上即是go運行時垃圾收回的全部,總結下整篇短文。

垃圾回收常見的算法有:引用計數、代齡、標記清理,go主要使用的標記清理,並在此基礎上對標記、清理都進行了併發,下面引用一張Go 1.12 Release的gc圖:

一分鐘系列:go語言運行時-垃圾回收

參考

https://reading.developerlearning.cn/reading/64-2019-10-24-go-runtime/


分享到:


相關文章: