聊聊什么是 Go Runtime.KeepAlive?
本文轉(zhuǎn)載自微信公眾號「HHFCodeRv」,作者haohongfan。轉(zhuǎn)載本文請聯(lián)系HHFCodeRv公眾號。
有些同學(xué)喜歡利用 runtime.SetFinalizer 模擬析構(gòu)函數(shù),當(dāng)變量被回收時,執(zhí)行一些回收操作,加速一些資源的釋放。在做性能優(yōu)化的時候這樣做確實有一定的效果,不過這樣做是有一定的風(fēng)險的。
比如下面這段代碼,初始化一個文件描述符,當(dāng) GC 發(fā)生時釋放掉無效的文件描述符。
- 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 := &Fileowocgsc
- 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
- }
上面這段代碼是對 go 官方文檔[1]的一個延伸,doSomeAllocation 會強制執(zhí)行 GC,當(dāng)我們執(zhí)行這段代碼時會出現(xiàn)下面的錯誤。
- panic: no such file or directory
- goroutine 1 [running]:
- main.openFile(0x107a65e, 0x5, 0x10d9220)
- main.go:20 +0xe5
- main.main()
- main.go:11 +0x3a
這是因為 syscall.Open 產(chǎn)生的文件描述符比較特殊,是個 int 類型,當(dāng)以值拷貝的方式在函數(shù)間傳遞時,并不會讓 File.d 產(chǎn)生引用關(guān)系,于是 GC 發(fā)生時就會調(diào)用 runtime.SetFinalizer(p, func(p *File) 導(dǎo)致文件描述符被 close 掉。
什么是 runtime.KeepAlive ?
如上面的例子,我們?nèi)绻拍茏屛募枋龇槐?gc 給釋放掉呢?其實很簡單,只需要調(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)我們真的需要使用時候,要注意使用是否合理。