什么是 Go runtime.KeepAlive?
有些同學(xué)喜歡利用 runtime.SetFinalizer 模擬析構(gòu)函數(shù),當(dāng)變量被回收時(shí),執(zhí)行一些回收操作,加速一些資源的釋放。在做性能優(yōu)化的時(shí)候這樣做確實(shí)有一定的效果,不過(guò)這樣做是有一定的風(fēng)險(xiǎn)的。
比如下面這段代碼,初始化一個(gè)文件描述符,當(dāng) GC 發(fā)生時(shí)釋放掉無(wú)效的文件描述符。
type?File?struct?{?d?int?}
func?main()?{
????p?:=?openFile("t.txt")
????content?:=?readFile(p.d)
????println("Here?is?the?content:?"+content)
}
func?openFile(path?string)?*File?{
????d,?err?:=?syscall.Open(path,?syscall.O_RDONLY,?0)
????if?err?!=?nil?{
??????panic(err)
????}
????p?:=?&Filego7utgvlrp
????runtime.SetFinalizer(p,?func(p?*File)?{
??????syscall.Close(p.d)
????})
????return?p
}
func?readFile(descriptor?int)?string?{
????doSomeAllocation()
????var?buf?[1000]byte
????_,?err?:=?syscall.Read(descriptor,?buf[:])
????if?err?!=?nil?{
??????panic(err)
????}
????return?string(buf[:])
}
func?doSomeAllocation()?{
????var?a?*int
????//?memory?increase?to?force?the?GC
????for?i:=?0;?i?10000000;?i++?{
??????i?:=?1
??????a?=?&i
????}
????_?=?a
}
上面這段代碼是對(duì) go 官方文檔[1]的一個(gè)延伸,doSomeAllocation 會(huì)強(qiáng)制執(zhí)行 GC,當(dāng)我們執(zhí)行這段代碼時(shí)會(huì)出現(xiàn)下面的錯(cuò)誤。
panic:?no?such?file?or?directory
goroutine?1?[running]:
main.openFile(0x107a65e,?0x5,?0x10d9220)
????????main.go:20?+0xe5
main.main()
????????main.go:11?+0x3a
這是因?yàn)?syscall.Open 產(chǎn)生的文件描述符比較特殊,是個(gè) int 類型,當(dāng)以值拷貝的方式在函數(shù)間傳遞時(shí),并不會(huì)讓 File.d 產(chǎn)生引用關(guān)系,于是 GC 發(fā)生時(shí)就會(huì)調(diào)用 runtime.SetFinalizer(p, func(p *File) 導(dǎo)致文件描述符被 close 掉。
什么是 runtime.KeepAlive ?
如上面的例子,我們?nèi)绻拍茏屛募枋龇槐?gc 給釋放掉呢?其實(shí)很簡(jiǎn)單,只需要調(diào)用 runtime.KeepAlive 即可。
func?main()?{
????p?:=?openFile("t.txt")
????content?:=?readFile(p.d)
????
????runtime.KeepAlive(p)
????println("Here?is?the?content:?"+content)
}
runtime.KeepAlive 能阻止 runtime.SetFinalizer 延遲發(fā)生,保證我們的變量不被 GC 所回收。
結(jié)論
正常情況下,runtime.KeepAlive,runtime.SetFinalizer 不應(yīng)該被濫用,當(dāng)我們真的需要使用時(shí)候,要注意使用是否合理。
《性能優(yōu)化 | Go Ballast 讓內(nèi)存控制更加絲滑》我們說(shuō)到如何讓整個(gè)程序的聲明周期內(nèi)維護(hù)一個(gè) slice 不被 gc 給回收掉,這里就用到了 runtime.KeepAlive。
這里還有有一篇關(guān)于 runtime.KeepAlive 的文檔[2],有興趣的可以看一下。
寫文章不易請(qǐng)大家?guī)兔c(diǎn)擊 在看,點(diǎn)贊,分享。
參考資料
go 官方文檔: https://pkg.go.dev/runtime#KeepAlive
[2]文檔: https://medium.com/a-journey-with-go/go-keeping-a-variable-alive-c28e3633673a
