文章來自網易雲社區
4 Golang垃圾回收的相關參數
4.1 觸發GC
gc觸發的時機:2分鐘或者內存佔用達到一個閾值(當前堆內存佔用是上次gc後對內存佔用的兩倍,當GOGC=100時)git
# 表示當前應用佔用的內存是上次GC時佔用內存的兩倍時,觸發GCexport GOGC=100
4.2 查看GC信息
export GODEBUG=gctrace=1
能夠查看gctrace信息。github
舉例:golang
gc 1 @0.008s 6%: 0.071+2.0+0.080 ms clock, 0.21+0.22/1.9/1.9+0.24 ms cpu, 4->4->3 MB, 5 MB goal, 4 P# command-line-argumentsgc 1 @0.001s 16%: 0.071+3.3+0.060 ms clock, 0.21+0.17/2.9/0.36+0.18 ms cpu, 4->4->4 MB, 5 MB goal, 4 Pgc 2 @0.016s 8%: 0.020+6.0+0.070 ms clock, 0.082+0.094/3.9/2.2+0.28 ms cpu, 8->9->8 MB, 9 MB goal, 4 Pgc 3 @0.046s 7%: 0.019+7.3+0.062 ms clock, 0.076+0.089/7.1/7.0+0.24 ms cpu, 14->16->14 MB, 17 MB goal, 4 Pgc 4 @0.092s 8%: 0.015+24+0.10 ms clock, 0.060+0.10/24/0.75+0.42 ms cpu, 25->27->24 MB, 28 MB goal, 4 P
每一個字段表示什麼信息能夠參考 golang doc算法
5 如何提升GC的性能
Golang的GC算法是固定的,用戶沒法去配置採用什麼算法,也無法像Java同樣配置年輕代、老年代的空間比例等。golang的GC相關的配置參數只有一個,即GOGC,用來表示觸發GC的條件。數據庫
目前來看,提升GC效率咱們惟一能作的就是減小垃圾的產生。因此說,這一章稱爲提升GC的性能也不太合適。下面咱們就主要討論一下,在golang中如何減小垃圾的產生,有哪些須要注意的方面。app
5.1 golang中的內存分配
參考官網Frequently Asked Questions (FAQ)ide
How do I know whether a variable is allocated on the heap or the stack?性能
From a correctness standpoint, you don't need to know. Each variable in Go exists as long as there are references to it. The storage location chosen by the implementation is irrelevant to the semantics of the language.ui
The storage location does have an effect on writing efficient programs. When possible, the Go compilers will allocate variables that are local to a function in that function's stack frame. However, if the compiler cannot prove that the variable is not referenced after the function returns, then the compiler must allocate the variable on the garbage-collected heap to avoid dangling pointer errors. Also, if a local variable is very large, it might make more sense to store it on the heap rather than the stack.this
In the current compilers, if a variable has its address taken, that variable is a candidate for allocation on the heap. However, a basic escape analysis recognizes some cases when such variables will not live past the return from the function and can reside on the stack.
咱們看一個例子有個直觀的認識:
1 package main2 3 import ()4 5 func foo() *int {6 var x int7 return &x8 }9 10 func bar() int {11 x := new(int)12 *x = 113 return *x14 }15 16 func big() {17 x := make([]int,0,20)18 y := make([]int,0,20000)19 20 len := 1021 z := make([]int,0,len)22 }23 24 func main() {25 26 }
# go build -gcflags='-m -l' test.go./test.go:7:12: &x escapes to heap ./test.go:6:9: moved to heap: x ./test.go:11:13: bar new(int) does not escape ./test.go:18:14: make([]int, 0, 20000) escapes to heap ./test.go:21:14: make([]int, 0, len) escapes to heap ./test.go:17:14: big make([]int, 0, 20) does not escape ./test.go:17:23: x declared and not used ./test.go:18:23: y declared and not used ./test.go:21:23: z declared and not used
5.2 sync.Pool對象池
sync.Pool主要是爲了重用對象,一方面縮短了申請空間的時間,另外一方面,還減輕了GC的壓力。不過它是一個臨時對象池,爲何這麼說呢?由於對象池中的對象會被GC回收。因此說,有狀態的對象,好比數據庫鏈接是不可以用sync.Pool來實現的。
use sync.Pool if you frequently allocate many objects of the same type and you want to save some allocation and garbage collection overhead. However, in the current implementation, any unreferenced sync.Pool objects are removed at each garbage collection cycle, so you can't use this as a long-lived free-list of objects. If you want a free-list that maintains objects between GC runs, you'll still have to build that yourself. This is only to reuse allocated objects between garbage collection cycles.
sync.Pool主要有兩個方法:
func (p *Pool) Get() interface{} { ... }func (p *Pool) Put(x interface{}) { ... }
Get方法是指從臨時對象池中申請對象,put是指把再也不使用的對象返回對象池,以便後續重用。若是咱們在使用Get申請新對象時pool中沒有可用的對象,那麼就會返回nil,除非設置了sync.Pool的New func:
type Pool struct { ... // New optionally specifies a function to generate // a value when Get would otherwise return nil. // It may not be changed concurrently with calls to Get. New func() interface{} }
另外,咱們不能對從對象池申請到的對象值作任何假設,多是New新生成的,多是被某個協程修改過放回來的。
一個比較好的使用sync.Pool的例子:
var DEFAULT_SYNC_POOL *SyncPool func NewPool() *SyncPool { DEFAULT_SYNC_POOL = NewSyncPool( 5, 30000, 2, ) return DEFAULT_SYNC_POOL } func Alloc(size int) []int64 { return DEFAULT_SYNC_POOL.Alloc(size) } func Free(mem []int64) { DEFAULT_SYNC_POOL.Free(mem) } // SyncPool is a sync.Pool base slab allocation memory pool type SyncPool struct { classes []sync.Pool classesSize []int minSize int maxSize int } func NewSyncPool(minSize, maxSize, factor int) *SyncPool { n := 0 for chunkSize := minSize; chunkSize <= maxSize; chunkSize *= factor { n++ } pool := &SyncPool{ make([]sync.Pool, n), make([]int, n), minSize, maxSize, } n = 0 for chunkSize := minSize; chunkSize <= maxSize; chunkSize *= factor { pool.classesSize[n] = chunkSize pool.classes[n].New = func(size int) func() interface{} { return func() interface{} { buf := make([]int64, size) return &buf } }(chunkSize) n++ } return pool } func (pool *SyncPool) Alloc(size int) []int64 { if size <= pool.maxSize { for i := 0; i < len(pool.classesSize); i++ { if pool.classesSize[i] >= size { mem := pool.classes[i].Get().(*[]int64) // return (*mem)[:size] return (*mem)[:0] } } } return make([]int64, 0, size) } func (pool *SyncPool) Free(mem []int64) { if size := cap(mem); size <= pool.maxSize { for i := 0; i < len(pool.classesSize); i++ { if pool.classesSize[i] >= size { pool.classes[i].Put(&mem) return } } } }
有一個開源的通用golang對象池實現,有興趣的能夠研究一下:Go Commons Pool,在此再也不贅述。
5.3 append
咱們先看一下append的基本用法。
nums:=make([]int,0,10)
建立切片,len=0,cap=10,底層實際上分配了10個元素大小的空間。在沒有append數據的狀況下,不能直接使用nums[index]。
nums:=make([]int,5,10)
建立切片,len=5,cap=10,底層實際上分配了10個元素大小的空間。在沒有append數據的狀況下,能夠直接使用nums[index],index的範圍是[0,4]。執行append操做的時候是從index=5的位置開始存儲的。
nums := make([]int,5)
若是沒有指定capacity,那麼cap與len相等。
nums = append(nums,10)
執行append操做的時候,nums的地址可能會改變,所以須要利用其返回值從新設置nums。至於nums的地址會不會改變,取決於尚未空間來存儲新的數據,若是沒有空閒空間了,那就須要申請cap*2的空間,將數據複製過去。
所以,咱們在使用append操做的時候,最好是設置一個比較合理的cap值,即根據本身的應用場景預申請大小合適的空間,避免無謂的不斷從新申請新空間,這樣能夠減小GC的壓力。
由append致使的內存飆升和GC壓力過大這個問題,須要特別注意一下。
參考文獻
1 https://en.wikipedia.org/wiki/Garbage_collection_(computer_science)
2 http://legendtkl.com/2017/04/28/golang-gc/
3 https://github.com/golang/proposal/blob/master/design/17503-eliminate-rescan.md
4 https://lengzzz.com/note/gc-in-golang
5 https://making.pusher.com/golangs-real-time-gc-in-theory-and-practice/
6 https://blog.twitch.tv/gos-march-to-low-latency-gc-a6fa96f06eb7
7 https://golang.org/doc/faq
8 《垃圾回收的算法與實現》 中村成楊 相川光. 編著
網易雲新用戶大禮包:https://www.163yun.com/gift
本文來自網易實踐者社區,經做者李嵐清受權發佈。