這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。
在實際的編程中,我們都希望每個對象釋放時執行一個方法,在該方法內執行一些計數、釋放或特定的要求,以往都是在對象指標置nil前調用一個特定的方法,golang提供了runtime.SetFinalizer函數,當GC準備釋放對象時,會回調該函數指定的方法,非常方便和有效。
不過值得注意的是,指標構成的 "迴圈引⽤" 加上 runtime.SetFinalizer 會導致記憶體泄露。
type Data struct { d [1024 * 100]byte o *Data}func test() { var a, b Data a.o = &b b.o = &a runtime.SetFinalizer(&a, func(d *Data) { fmt.Printf("a %p final.\n", d) }) runtime.SetFinalizer(&b, func(d *Data) { fmt.Printf("b %p final.\n", d) })}func main() { for { test() time.Sleep(time.Millisecond) }}
輸出:
$ go build -gcflags "-N -l" && GODEBUG="gctrace=1" ./testgc11(1): 2+0+0 ms, 104 -> 104 MB 1127 -> 1127 (1180-53) objectsgc12(1): 4+0+0 ms, 208 -> 208 MB 2151 -> 2151 (2226-75) objectsgc13(1): 8+0+1 ms, 416 -> 416 MB 4198 -> 4198 (4307-109) objects
記憶體回收行程能正確處理 "指標迴圈引⽤",但⽆法確定 Finalizer 依賴次序,也就⽆法調⽤ Finalizer 函數,這會導致目標對象⽆法變成不可達狀態,其所佔⽤記憶體⽆法被回收。