24.3 垃圾回收和 SetFinalizer

Go 语言开发者一般不需要写代码来释放不再使用的变量或结构体占用的内存,在 Go语言运行时有一个独立的进程,即垃圾收集器(GC),会专门处理这些事情,它搜索不再使用的变量然后释放它们占用的内存,这是自动垃圾回收;还有一种是主动垃圾回收,通过显式调用 runtime.GC()来实现。

通过调用 runtime.GC() 函数可以显式的触发 GC,这在某些的场景下非常有用,比如当内存资源不足时调用 runtime.GC(),它会在此函数执行的点上立即释放一大片内存,但此时程序可能会有短时的性能下降(因为 GC 进程在执行)。

下面代码中的 func (p *Person) NewOpen() 在某些情况下非常有必要这样处理,比如某些资源占用申请,开发人员可能忘记使用defer Close()来销毁处理,但通过SetFinalizer,如果GC自动运行或者手动运行GC,则都能及时销毁这些资源,释放占用的内存而避免内存泄漏。

GC过程中重要的函数 func SetFinalizer(obj interface{}, finalizer interface{}) 有两个参数,参数一:obj必须是指针类型。参数二:finalizer是一个函数,其参数类型是obj的类型,其没有返回值。

package main
import (
	"log"
	"runtime"
	"time"
)
type Person struct {
	Name string
	Age  int
}
func (p *Person) Close() {
	p.Name = "NewName"
	log.Println(p)
	log.Println("Close")
}
func (p *Person) NewOpen() {
	log.Println("Init")
	runtime.SetFinalizer(p, (*Person).Close)
}
func Tt(p *Person) {
	p.Name = "NewName"
	log.Println(p)
	log.Println("Tt")
}
// 查看内存情况
func Mem(m *runtime.MemStats) {
	runtime.ReadMemStats(m)
	log.Printf("%d Kb\n", m.Alloc/1024)
}
func main() {
	var m runtime.MemStats
	Mem(&m)
	var p *Person = &Person{Name: "lee", Age: 4}
	p.NewOpen()
	log.Println("Gc完成第一次")
	log.Println("p:", p)
	runtime.GC()
	time.Sleep(time.Second * 5)
	Mem(&m)
	var p1 *Person = &Person{Name: "Goo", Age: 9}
	runtime.SetFinalizer(p1, Tt)
	log.Println("Gc完成第二次")
	time.Sleep(time.Second * 2)
	runtime.GC()
	time.Sleep(time.Second * 2)
	Mem(&m)
}