返回介绍

上卷 程序设计

中卷 标准库

下卷 运行时

源码剖析

附录

SetFinalizer

发布于 2024-10-12 19:15:54 字数 1634 浏览 0 评论 0 收藏 0

为对象设置一个析构函数,在被 GC 回收时执行。

当 GC 发现目标对象不可达时,它先解除析构函数关联,并在一个特定 goroutine 执行该函数。为使析构函数正确执行,目标对象被重新标记为可达。等下次回收时,已没有关联析构函数,目标对象被正确回收。

  • 无法确定执行时间,不能保证一定被执行。
  • 无法保证零长度对象的析构函数被执行。
  • 全局变量(非堆存储)不应有析构函数。
  • 析构函数在单个 goroutine 运行,不应阻塞或执行太久。
  • KeepAlive 标记可达位置,阻止析构函数执行。
  • 调用 SetFinalizer(obj, nil) 清除析构函数。
package main

import (
	"fmt"
	"runtime"
	"time"
)

func main() {
	d := make([]byte, 100<<20)
	fmt.Printf("%p\n", &d) 

	runtime.SetFinalizer(&d, func(o *[]byte) { 
		fmt.Printf("%p drop!\n", o) 
	})

	for i := 0; i < 5; i++ {
		time.Sleep(time.Second)
		runtime.GC()
	}

	// runtime.KeepAlive(&d)
}

/*

$ go build && GODEBUG=gctrace=1 ./test

0xc00009a018
0xc00009a018 drop!

*/

验证循环引用(cycle reference)导致无法回收,造成内存泄漏。

package main

import (
	"runtime"
	"time"
)

func main() {
	for {
		func() {
			type data struct {
				x *data
				_ [1<<20]byte
			}

			a, b := data{}, data{}
			a.x = &b
			b.x = &a

			runtime.SetFinalizer(&a, func(*data) { println("a!") })
			runtime.SetFinalizer(&b, func(*data) { println("b!") })
		}()

		runtime.GC()
		time.Sleep(time.Millisecond * 100)
	}
}

/*

$ go build && GODEBUG=gctrace=1 ./test

gc 26 @2.678s 0%: ..., 102->102->104 MB, 203 MB goal, ..., 2 P (forced)
gc 27 @2.788s 0%: ..., 106->106->108 MB, 211 MB goal, ..., 2 P (forced)
gc 28 @2.896s 0%: ..., 110->110->112 MB, 219 MB goal, ..., 2 P (forced)
gc 29 @3.005s 0%: ..., 114->114->116 MB, 228 MB goal, ..., 2 P (forced)

*/

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文