什麼是 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 := &File{d}
    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 會強制執行 GC,當我們執行這段代碼時會出現下面的錯誤。

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 產生的文件描述符比較特殊,是個 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

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/5XMmVqdcji8jpRnC2p0F3w