什么是 Go runtime.KeepAlive?
有些同學喜歡利用 runtime.SetFinalizer 模擬析構函數,當變量被回收時,執行一些回收操作,加速一些資源的釋放。在做性能優化的時候這樣做確實有一定的效果,不過這樣做是有一定的風險的。
比如下面這段代碼,初始化一個文件描述符,當 GC 發生時釋放掉無效的文件描述符。
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?:=?&Fileze8trgl8bvbqruntime.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?GCfor?i:=?0;?i?<?10000000;?i++?{i?:=?1a?=?&i}_?=?a }上面這段代碼是對 go 官方文檔[1]的一個延伸,doSomeAllocation 會強制執行 GC,當我們執行這段代碼時會出現下面的錯誤。
panic:?no?such?file?or?directorygoroutine?1?[running]: main.openFile(0x107a65e,?0x5,?0x10d9220)main.go:20?+0xe5 main.main()main.go:11?+0x3a這是因為 syscall.Open 產生的文件描述符比較特殊,是個 int 類型,當以值拷貝的方式在函數間傳遞時,并不會讓 File.d 產生引用關系,于是 GC 發生時就會調用 runtime.SetFinalizer(p, func(p *File) 導致文件描述符被 close 掉。
什么是 runtime.KeepAlive ?
如上面的例子,我們如果才能讓文件描述符不被 gc 給釋放掉呢?其實很簡單,只需要調用 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 延遲發生,保證我們的變量不被 GC 所回收。
結論
正常情況下,runtime.KeepAlive,runtime.SetFinalizer 不應該被濫用,當我們真的需要使用時候,要注意使用是否合理。
《性能優化 | Go Ballast 讓內存控制更加絲滑》我們說到如何讓整個程序的聲明周期內維護一個 slice 不被 gc 給回收掉,這里就用到了 runtime.KeepAlive。
這里還有有一篇關于 runtime.KeepAlive 的文檔[2],有興趣的可以看一下。
寫文章不易請大家幫忙點擊 在看,點贊,分享。
參考資料
[1]
go 官方文檔: https://pkg.go.dev/runtime#KeepAlive
[2]文檔: https://medium.com/a-journey-with-go/go-keeping-a-variable-alive-c28e3633673a
總結
以上是生活随笔為你收集整理的什么是 Go runtime.KeepAlive?的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 飞哥:十年光阴如梭
- 下一篇: 开发中的坑:MQ 也能做 RPC 调用?