mysql 变量生命周期_Go: 延长变量的生命周期

本文基于 Go 1.13。
在 Go 中,我們不需要自己管理內存分配和釋放。然而,有些時候我們需要對程序進行更細粒度的控制。Go 運行時提供了很多種控制運行時狀態及其與內存管理器之間相互影響的方式。本文中,我們來審查讓變量不被 GC 回收的能力。
## 場景
我們來看一個基于 [Go 官方文檔](https://golang.org/pkg/runtime/#KeepAlive) 的例子:
```go
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 := &Fileozvdkddzhkzd
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
}
```
[源碼地址](https://gist.githubusercontent.com/blanchonvincent/a247b6c2af559b62f93377b5d7581b7f/raw/6488ec2a36c28c46f942b7ac8f24af4e75c19a2f/main.go)
這個程序中一個函數打開文件,另一個函數讀取文件。代表文件的結構體注冊了一個 finalizer,在 gc 釋放結構體時自動關閉文件。運行這個程序,會出現 panic:
```bash
panic: bad file descriptor
goroutine 1 [running]:
main.readFile(0x3, 0x5, 0xc000078008)
main.go:42 +0x103
main.main()
main.go:14 +0x4b
exit status 2
```
下面是流程圖:
- 打開文件,返回一個文件描述符
- 這個文件描述符被傳遞給讀取文件的函數
- 這個函數首先做一些繁重的工作:

allocate 函數觸發 gc:

因為文件描述符是個整型,并以副本傳遞,所以打開文件的函數返回的結構體 `*File*` 不再被引用。Gc 把它標記為可以被回收的。之后觸發這個變量注冊的 finalizer,關閉文件。
然后,主協程讀取文件:

因為文件已經被 finalizer 關閉,所以會出現 panic。
## 讓變量不被回收
`runtime` 包暴露了一個方法,用來在 Go 程序中避免出現這種情況,并顯式地聲明了讓變量不被回收。在運行到這個調用這個方法的地方之前,gc 不會清除指定的變量。下面是加了對這個方法的調用的新代碼:

在運行到 `keepAlive` 方法之前,gc 不能回收變量 `p`。如果你再運行一次程序,它會正常讀取文件并成功終止。
## 追本逐源
`keepAlive` 方法本身沒有做什么:

運行時,Go 編譯器會以很多種方式優化代碼:函數內聯,死碼消除,等等。這個函數不會被內聯,Go 編譯器可以輕易地探測到哪里調用了 `keepAlive`。編譯器很容易追蹤到調用它的地方,它會發出一個特殊的 SSA 指令,以此來確保它不會被 gc 回收。

在生成的 SSA 代碼中也可以看到這個 SSA 指令:

在我的文章 [Go 編譯器概述](https://medium.com/a-journey-with-go/go-overview-of-the-compiler-4e5a153ca889) 中你可以看到更多關于 Go 編譯器和 SSA 的信息。
本文由 GCTT 原創編譯,Go語言中文網 榮譽推出
本文由 GCTT 原創翻譯,Go語言中文網 首發。也想加入譯者行列,為開源做一些自己的貢獻么?歡迎加入 GCTT!
翻譯工作和譯文發表僅用于學習和交流目的,翻譯工作遵照 CC-BY-NC-SA 協議規定,如果我們的工作有侵犯到您的權益,請及時聯系我們。
歡迎遵照 CC-BY-NC-SA 協議規定 轉載,敬請在正文中標注并保留原文/譯文鏈接和作者/譯者等信息。
文章僅代表作者的知識和看法,如有不同觀點,請樓下排隊吐槽
有疑問加站長微信聯系(非本文作者))
總結
以上是生活随笔為你收集整理的mysql 变量生命周期_Go: 延长变量的生命周期的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: mysql自增字段_MySQL自增字段的
- 下一篇: mysql索引 物理文件_MySQL体系