<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          Go: 延長(zhǎng)變量的生命周期

          共 3289字,需瀏覽 7分鐘

           ·

          2021-05-10 17:58

          點(diǎn)擊上方藍(lán)色“Go語(yǔ)言中文網(wǎng)”關(guān)注,每天一起學(xué) Go

          Illustration created for “A Journey With Go”, made from the original Go Gopher, created by Renee French.

          本文基于 Go 1.13。

          在 Go 中,我們不需要自己管理內(nèi)存分配和釋放。然而,有些時(shí)候我們需要對(duì)程序進(jìn)行更細(xì)粒度的控制。Go 運(yùn)行時(shí)提供了很多種控制運(yùn)行時(shí)狀態(tài)及其與內(nèi)存管理器之間相互影響的方式。本文中,我們來(lái)審查讓變量不被 GC 回收的能力。

          場(chǎng)景

          我們來(lái)看一個(gè)基于 Go 官方文檔[1] 的例子:

          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
          }

          源碼地址[2]

          這個(gè)程序中一個(gè)函數(shù)打開(kāi)文件,另一個(gè)函數(shù)讀取文件。代表文件的結(jié)構(gòu)體注冊(cè)了一個(gè) finalizer,在 gc 釋放結(jié)構(gòu)體時(shí)自動(dòng)關(guān)閉文件。運(yùn)行這個(gè)程序,會(huì)出現(xiàn) panic:

          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

          下面是流程圖:

          • 打開(kāi)文件,返回一個(gè)文件描述符
          • 這個(gè)文件描述符被傳遞給讀取文件的函數(shù)
          • 這個(gè)函數(shù)首先做一些繁重的工作:

          圖 01

          allocate 函數(shù)觸發(fā) gc:

          02.png

          因?yàn)槲募枋龇莻€(gè)整型,并以副本傳遞,所以打開(kāi)文件的函數(shù)返回的結(jié)構(gòu)體 *File* 不再被引用。Gc 把它標(biāo)記為可以被回收的。之后觸發(fā)這個(gè)變量注冊(cè)的 finalizer,關(guān)閉文件。

          然后,主協(xié)程讀取文件:

          03.png

          因?yàn)槲募呀?jīng)被 finalizer 關(guān)閉,所以會(huì)出現(xiàn) panic。

          讓變量不被回收

          runtime 包暴露了一個(gè)方法,用來(lái)在 Go 程序中避免出現(xiàn)這種情況,并顯式地聲明了讓變量不被回收。在運(yùn)行到這個(gè)調(diào)用這個(gè)方法的地方之前,gc 不會(huì)清除指定的變量。下面是加了對(duì)這個(gè)方法的調(diào)用的新代碼:

          04.png

          在運(yùn)行到 keepAlive 方法之前,gc 不能回收變量 p。如果你再運(yùn)行一次程序,它會(huì)正常讀取文件并成功終止。

          追本逐源

          keepAlive 方法本身沒(méi)有做什么:

          05.png

          運(yùn)行時(shí),Go 編譯器會(huì)以很多種方式優(yōu)化代碼:函數(shù)內(nèi)聯(lián),死碼消除,等等。這個(gè)函數(shù)不會(huì)被內(nèi)聯(lián),Go 編譯器可以輕易地探測(cè)到哪里調(diào)用了 keepAlive。編譯器很容易追蹤到調(diào)用它的地方,它會(huì)發(fā)出一個(gè)特殊的 SSA 指令,以此來(lái)確保它不會(huì)被 gc 回收。

          06.png

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

          07.png

          在我的文章 Go 編譯器概述[3] 中你可以看到更多關(guān)于 Go 編譯器和 SSA 的信息。


          via: https://medium.com/a-journey-with-go/go-keeping-a-variable-alive-c28e3633673a

          作者:Vincent Blanchon[4]譯者:lxbwolf[5]校對(duì):polaris1119[6]

          本文由 GCTT[7] 原創(chuàng)編譯,Go 中文網(wǎng)[8] 榮譽(yù)推出

          參考資料

          [1]

          Go 官方文檔: https://golang.org/pkg/runtime/#KeepAlive

          [2]

          源碼地址: https://gist.githubusercontent.com/blanchonvincent/a247b6c2af559b62f93377b5d7581b7f/raw/6488ec2a36c28c46f942b7ac8f24af4e75c19a2f/main.go

          [3]

          Go 編譯器概述: https://medium.com/a-journey-with-go/go-overview-of-the-compiler-4e5a153ca889

          [4]

          Vincent Blanchon: https://medium.com/@blanchon.vincent

          [5]

          lxbwolf: https://github.com/lxbwolf

          [6]

          polaris1119: https://github.com/polaris1119

          [7]

          GCTT: https://github.com/studygolang/GCTT

          [8]

          Go 中文網(wǎng): https://studygolang.com/



          推薦閱讀


          福利

          我為大家整理了一份從入門到進(jìn)階的Go學(xué)習(xí)資料禮包,包含學(xué)習(xí)建議:入門看什么,進(jìn)階看什么。關(guān)注公眾號(hào) 「polarisxu」,回復(fù) ebook 獲?。贿€可以回復(fù)「進(jìn)群」,和數(shù)萬(wàn) Gopher 交流學(xué)習(xí)。

          瀏覽 100
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  久久久国产精品视频 | 国产综合视频 | 大香蕉在线观看成人 | 波多野结衣一区二区三区中文字幕 | 大鸡巴操小穴视频 |