Golang——垃圾回收GC
Go 垃圾回收原理
Golang源碼探索(三) GC的實現原理
- 引用計數:對每個對象維護一個引用計數,當引用該對象的對象被銷毀時,引用計數減1,當引用計數器為0是回收該對象。
- 優點:對象可以很快的被回收,不會出現內存耗盡或達到某個閥值時才回收。
- 缺點:不能很好的處理循環引用,而且實時維護引用計數,有也一定的代價。
- 代表語言:Python、PHP、Swift
- 標記-清除:從根變量開始遍歷所有引用的對象,引用的對象標記為"被引用",沒有被標記的進行回收。
- 優點:解決了引用計數的缺點。
- 缺點:需要STW,即要暫時停掉程序運行。
- 代表語言:Golang(其采用三色標記法)
- 分代收集:按照對象生命周期長短劃分不同的代空間,生命周期長的放入老年代,而短的放入新生代,不同代有不能的回收算法和回收頻率。
- 優點:回收性能好
- 缺點:算法復雜
- 代表語言: JAVA
標記
在之前的Go語言——內存管理一文中,分析過span是內存管理的最小單位,所以猜測gc的粒度也是span。
type mspan struct {// allocBits and gcmarkBits hold pointers to a span's mark and// allocation bits. The pointers are 8 byte aligned.// There are three arenas where this data is held.// free: Dirty arenas that are no longer accessed// and can be reused.// next: Holds information to be used in the next GC cycle.// current: Information being used during this GC cycle.// previous: Information being used during the last GC cycle.// A new GC cycle starts with the call to finishsweep_m.// finishsweep_m moves the previous arena to the free arena,// the current arena to the previous arena, and// the next arena to the current arena.// The next arena is populated as the spans request// memory to hold gcmarkBits for the next GC cycle as well// as allocBits for newly allocated spans.//// The pointer arithmetic is done "by hand" instead of using// arrays to avoid bounds checks along critical performance// paths.// The sweep will free the old allocBits and set allocBits to the// gcmarkBits. The gcmarkBits are replaced with a fresh zeroed// out memory.allocBits *gcBitsgcmarkBits *gcBits }bitmap
如圖所示,通過gcmarkBits位圖標記span的塊是否被引用。對應內存分配中的bitmap區。
三色標記
- 灰色:對象已被標記,但這個對象包含的子對象未標記
- 黑色:對象已被標記,且這個對象包含的子對象也已標記,gcmarkBits對應的位為1(該對象不會在本次GC中被清理)
- 白色:對象未被標記,gcmarkBits對應的位為0(該對象將會在本次GC中被清理)
例如,當前內存中有A~F一共6個對象,根對象a,b本身為棧上分配的局部變量,根對象a、b分別引用了對象A、B, 而B對象又引用了對象D,則GC開始前各對象的狀態如下圖所示:
STW
stop the world是gc的最大性能問題,對于gc而言,需要停止所有的內存變化,即停止所有的goroutine,等待gc結束之后才恢復。
觸發
- 閾值:默認內存擴大一倍,啟動gc
- 定期:默認2min觸發一次gc,src/runtime/proc.go:forcegcperiod
- 手動:runtime.gc()
go
目前整個GC流程會進行兩次STW(Stop The World), 第一次是Mark階段的開始, 第二次是Mark Termination階段.
- 第一次STW會準備根對象的掃描, 啟動寫屏障(Write Barrier)和輔助GC(mutator assist).
- 第二次STW會重新掃描部分根對象, 禁用寫屏障(Write Barrier)和輔助GC(mutator assist).
需要注意的是, 不是所有根對象的掃描都需要STW, 例如掃描棧上的對象只需要停止擁有該棧的G.
從go 1.9開始, 寫屏障的實現使用了Hybrid Write Barrier, 大幅減少了第二次STW的時間.
寫屏障
因為go支持并行GC, GC的掃描和go代碼可以同時運行, 這樣帶來的問題是GC掃描的過程中go代碼有可能改變了對象的依賴樹。
例如開始掃描時發現根對象A和B, B擁有C的指針。
為了避免這個問題, go在GC的標記階段會啟用寫屏障(Write Barrier).
啟用了寫屏障(Write Barrier)后,
即使A可能會在稍后丟掉C, 那么C就在下一輪回收。
開啟寫屏障之后,當指針發生改變, GC會認為在這一輪的掃描中這個指針是存活的, 所以放入灰色。
root
首先標記root跟對象,根對象的子對象也是存活的。
根對象包括:全局變量,各個G stack上的變量等。
轉載于:https://www.cnblogs.com/saryli/p/10105393.html
總結
以上是生活随笔為你收集整理的Golang——垃圾回收GC的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: DAY97 - Rest Framewo
- 下一篇: 在echarts图表外调用echarts