本文转载自微信公众号「HHFCodeRv」作者haohongfan。转载本文请联系HHFCodeRv公众号。有些同学喜欢用runtime.SetFinalizer来模拟析构函数。当变量被回收时,进行一些回收操作,加速释放一些资源。这样做在做性能优化的时候确实有一定的效果,但是这样做也存在一定的风险。例如,下面的代码初始化一个文件描述符,并在GC发生时释放无效的文件描述符。typeFilestruct{dint}funcmain(){p:=openFile("t.txt")content:=readFile(p.d)println("Hereisthecontent:"+content)}funcopenFile(pathstring)*File{d,err:=syscall.Open(path,syscall.O_RDONLY,0)iferr!=nil{panic(err)}p:=&File{d}runtime.SetFinalizer(p,func(p*File){syscall.Close(p.d)})returnp}funcreadFile(descriptorint)string{doSomeAllocation()varbuf[1000]byte_,err:=syscall.Read(descriptor,buf[:])iferr!=nil{panic(err)}returnstring(buf[:])}funcdoSomeAllocation(){vara*int//memoryinincreasetoforcetheGCfori:=0;i<10000000;i++{i:=1a=&i}_=a}上面代码是go官方文档[1]的扩展,doSomeAllocation会强制GC,如下当我们执行这段代码时发生错误。panic:nosuchfileordirectorygoroutine1[running]:main.openFile(0x107a65e,0x5,0x10d9220)main.go:20+0xe5main.main()main.go:11+0x3a这是因为syscall.Open生成的文件描述符比较特殊,是一个int类型,当以值拷贝的形式在函数之间传递时,不会导致File.d产生引用关系,所以GC发生时会调用runtime.SetFinalizer(p,func(p*File)导致文件descriptortobeclosedclose.什么是runtime.KeepAlive?如上例,如果我们可以阻止文件描述符被gc释放呢?其实很简单,调用runtime.KeepAlive.funcmain(){p:=openFile("t.txt")content:=readFile(p.d)runtime.KeepAlive(p)println("Hereisthecontent:"+content)}runtime.KeepAlive可以防止runtime.SetFinalizer的延迟发生,保证我们的变量会不会被GC回收结论一般情况下,runtime.KeepAlive和runtime.SetFinalizer不应该被滥用,当我们真正需要使用它们,要注意使用是否合理。
