<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垃圾回收系列之十一:finalizer的妙用

          共 2242字,需瀏覽 5分鐘

           ·

          2021-04-06 19:39

          我只知道,如果在這里退縮了一步,那么過(guò)去那些重要的誓言、約定,就全部會(huì)消失不見(jiàn),而我再也無(wú)法回到這里了。

              在面向?qū)ο蟮恼Z(yǔ)言中,析構(gòu)函數(shù)會(huì)在對(duì)象被銷毀的時(shí)候調(diào)用。finalizer是Go語(yǔ)言中的析構(gòu)函數(shù),可以由runtime.SetFinalizer函數(shù)將對(duì)象與finalizer函數(shù)綁在一起。當(dāng)對(duì)象不再被使用時(shí),可調(diào)用一個(gè)綁定的析構(gòu)函數(shù)。

              一個(gè)極小的功能也可能有足夠驚艷的表現(xiàn)。在本文中,筆者將介紹finalizer應(yīng)用的場(chǎng)景和陷阱。我們知道Go的垃圾回收足夠的強(qiáng)大,但是卻沒(méi)有辦法管理所有的內(nèi)存,例如CGO的內(nèi)存、關(guān)閉操作系統(tǒng)資源描述符等。如果我們可以將finalizer函數(shù)與具體代表資源描述符的對(duì)象關(guān)聯(lián)起來(lái),那么就可以實(shí)現(xiàn)自動(dòng)關(guān)閉操作系統(tǒng)資源描述符的功能。實(shí)際上,GO語(yǔ)言就是這樣做的。在新建操作系統(tǒng)文件描述符時(shí),就完成了這一綁定。

          func (fd *netFD) setAddr(laddr, raddr Addr) {
          fd.laddr = laddr
          fd.raddr = raddr
          runtime.SetFinalizer(fd, (*netFD).Close)
          }

          func (fd *netFD) Close() error {
          runtime.SetFinalizer(fd, nil)
          return fd.pfd.Close()
          }

              所以,我們其實(shí)不用close資源,也不會(huì)帶來(lái)資源泄露的困擾,但是手動(dòng)close 資源描述符仍然是有必要的。因?yàn)檫@會(huì)顯式的告訴我們資源已經(jīng)在此處不被使用了。

              當(dāng)我們?cè)趯?shí)際中書(shū)寫(xiě)CGO程序時(shí),在Go語(yǔ)言調(diào)用C函數(shù)的時(shí)候,C函數(shù)分配的內(nèi)存并不受到Go垃圾回收的管理,這時(shí)我們常常借助defer 在函數(shù)調(diào)用結(jié)束時(shí)手動(dòng)釋放內(nèi)存。如下所示,在defer釋放C結(jié)構(gòu)體中的指針。

          package main

          // #include <stdio.h>
          // typedef struct {
          // char *msg;
          // } myStruct;
          // void myFunc(myStruct *strct) {
          // printf("Hello %s!\n", strct->msg);
          // }

          import "C"
          func main() {
          msg := C.myStruct{C.CString("world")}
          defer C.free(msg.msg)
          C.myFunc(&msg)
          }

              將其修改為finalizer的形式如下,這種方式將垃圾回收從函數(shù)結(jié)束后的defer 延遲到了垃圾回收階段,這延緩了垃圾回收對(duì)資源的釋放,在某些情況下實(shí)現(xiàn)了對(duì)內(nèi)存的單獨(dú)管理。需要注意的是,其中runtime.KeepAlive保證了finalizer的調(diào)用只能發(fā)生在該函數(shù)之后,這是為了避免一些嚴(yán)重的問(wèn)題,例如C.myFunc(msg.msg) 使用了msg.msg字段,這時(shí)由于msg已經(jīng)不再被引用,立即調(diào)用了finalizer,對(duì)msg.msg字段進(jìn)行了釋放,這時(shí)如果還在執(zhí)行C.myFunc,接觸msg.msg指針時(shí)就會(huì)報(bào)錯(cuò)。

          import "C"
          func main() {
          msg := C.myStruct{C.CString("world")}
          runtime.SetFinalizer(&msg, func(t *C.myStruct) {
          C.free(unsafe.Pointer(t.msg))
          })
          C.myFunc(msg.msg)
          runtime.KeepAlive(&msg)
          }

              這種方式看上去很丑,對(duì)于一般的開(kāi)發(fā)者來(lái)講也比較的困擾,但是有用嗎?毫無(wú)疑問(wèn)是有用的,想象一下你希望開(kāi)發(fā)一個(gè)第三方庫(kù), 但是返回給用戶的對(duì)象引用了一個(gè)C分配的內(nèi)存對(duì)象,那么我們無(wú)法得知用戶什么時(shí)候會(huì)放棄使用該內(nèi)存,我們當(dāng)然不能夠在defer中釋放資源。但是我們又希望借助于自動(dòng)垃圾回收的功能,不用開(kāi)發(fā)者手動(dòng)的去調(diào)用free資源的函數(shù),這時(shí)這個(gè)功能就派上了用場(chǎng)。

              在這里,要提到finalizer的一個(gè)陷阱。由于垃圾回收時(shí)調(diào)用finalizer很有可能是在另一個(gè)線程中執(zhí)行的,但有些資源可能不是線程安全的,例如在進(jìn)行GPU編程的時(shí)候。這時(shí)的一種解決辦法是,在finalizer函數(shù)中并不一定要執(zhí)行實(shí)際的資源釋放,可以只是將當(dāng)前指針用哈希表存儲(chǔ)起來(lái),并由綁定在統(tǒng)一線程上的協(xié)程定時(shí)釋放。



          推薦閱讀


          福利

          我為大家整理了一份從入門到進(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í)。

          瀏覽 70
          點(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>
                  台湾中文无码 | 日韩性爱视频在线观看 | 俺也去官网,国产97碰公开 | 天天爽夜夜爽精品成人免费 | 久久久久久黄片 |