Go如何巧妙使用runtime.SetFinalizer


runtime.SetFinalizer

func SetFinalizer(obj interface{}, finalizer interface{}) SetFinalizer sets the finalizer associated with obj to the provided finalizer function. When the garbage collector finds an unreachable block with an associated finalizer, it clears the association and runs finalizer(obj) in a separate goroutine. This makes obj reachable again, but now without an associated finalizer. Assuming that SetFinalizer is not called again, the next time the garbage collector sees that obj is unreachable, it will free obj

 

上面是官方文檔對SetFinalizer的一些解釋,主要含義是對象可以關聯一個SetFinalizer函數, 當gc檢測到unreachable對象有關聯的SetFinalizer函數時,會執行關聯的SetFinalizer函數, 同時取消關聯。 這樣當下一次gc的時候,對象重新處於unreachable狀態並且沒有SetFinalizer關聯, 就會被回收。

仔細看文檔,還有幾個需要注意的點:

* 即使程序正常結束或者發生錯誤, 但是在對象被 gc 選中並被回收之前,SetFinalizer 都不會執行, 所以不要在SetFinalizer中執行將內存中的內容flush到磁盤這種操作

* SetFinalizer 最大的問題是延長了對象生命周期。在第一次回收時執行 Finalizer 函數,且目標對象重新變成可達狀態,直到第二次才真正 “銷毀”。這對於有大量對象分配的高並發算法,可能會造成很大麻煩

* 指針構成的 "循環引⽤" 加上 runtime.SetFinalizer 會導致內存泄露

 

package main

import (
	"log"
	"runtime"
	"time"
)

type test int

func findRoad(t *test) {
	// 一般在這里進行資源回收
	log.Println("test:", *t)
}

func entry() {
	var rd test = test(1111)
	r := &rd
	// 解除綁定並執行對應函數下一次gc在進行清理
	runtime.SetFinalizer(r, findRoad)
}

func main() {
	entry()
	for i := 0; i < 10; i++ {
		time.Sleep(time.Second)
		runtime.GC()
	}
}

  

函數原型

func SetFinalizer(x, f interface{})

  

SetFinalizer將x的終止器設置為f。當垃圾收集器發現一個不能接觸的(即引用計數為零,程序中不能再直接或間接訪問該對象)具有終止器的塊時,它會清理該關聯(對象到終止器)並在獨立go程調用f(x)。這使x再次可以接觸,但沒有了綁定的終止器。如果SetFinalizer沒有被再次調用,下一次垃圾收集器將視x為不可接觸的,並釋放x。

SetFinalizer(x, nil)會清理任何綁定到x的終止器。

參數x必須是一個指向通過new申請的對象的指針,或者通過對復合字面值取址得到的指針。參數f必須是一個函數,它接受單個可以直接用x類型值賦值的參數,也可以有任意個被忽略的返回值。如果這兩條任一條不被滿足,本函數就會中斷程序。

終止器會按依賴順序執行:如果A指向B,兩者都有終止器,且它們無法從其它方面接觸,只有A的終止器執行;A被釋放后,B的終止器就可以執行。如果一個循環結構包含一個具有終止器的塊,該循環不能保證會被當垃圾收集,終止器也不能保證會執行;因為沒有尊重依賴關系的順序。

x的終止器會在x變為不可接觸之后的任意時間被調度執行。不保證終止器會在程序退出前執行,因此一般終止器只用於在長期運行的程序中釋放關聯到某對象的非內存資源。例如,當一個程序丟棄一個os.File對象時沒有調用其Close方法,該os.File對象可以使用終止器去關閉對應的操作系統文件描述符。但依靠終止器去刷新內存中的I/O緩沖如bufio.Writer是錯誤的,因為緩沖不會在程序退出時被刷新。

如果*x的大小為0字節,不保證終止器會執行。

一個程序會有單獨一個go程順序執行所有的終止器。如果一個終止器必須運行較長時間,它應該在內部另開go程執行該任務。

 

 

 
       


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM