Golang对象复用静态代码检查工具

一、对象复用

在高并发的场景下使用golang,优化GC都会无法回避的问题。搜索「golang 垃圾回收优化」出来的结果基本上都会提及对象复用的方式,在实践中也确实很多高性能的开源库大量使用对象复用来优化程序,比如fasthttp。

简单的例子

使用对象复用分为三个环节,1.初始化pool;2.获取对象;3.清空对象+归还对象。

package main
import (
    "fmt"
    "sync"
)

var pool *sync.Pool

type Person struct {
    Name string
}

func (p *Person)Reset() {
  p.Name = ""
}

func initPool() {
    pool = &sync.Pool {
        New: func()interface{} {
            fmt.Println("Creating a new Person")
            return new(Person)
        },
    }
}

func main() {
    // 1. 初始化池
    initPool()
    // 2. 获取对象
    p := pool.Get().(*Person)

    p.Name = "first"

    // 3. 清空对象 + 归还对象
    p.Reset()
    pool.Put(p)

}

二、对象复用的坑

在上面的例子中,如果我们把Reset函数注释了,那么放回池子里的Person对象的Name是不干净的,很有可能会影响下一个使用的地方。不过实际使用中忘记调用Reset方法的比较少,这是对象复用的基本意识,真正容易遗漏的是Reset方法中的实现。

在项目实践中,某些结构体定义的字段会比较多,而项目又一直在迭代,结构体的字段会发生新增、修改或删除,如果新增了字段,但Reset方法漏了新增对应的Reset语句,那么程序就很可能会出现数据篡乱的问题,而且这种问题不是稳定必现的,如果未意识到是字段漏了重置的问题,定位起来让人很头疼。

三、工具比人靠谱

想要避免出现这种问题,完全依赖个人注意肯定是不靠谱的,一个项目可能有多个人维护,团队也可能会有新人,难以确保每个人都能顾及到所有的对象复用。于是我开发了一个静态代码检查的工具 structreset,用于检查对象复用的结构体的Reset方法,是否包含所有结构体字段的重置。当然这里的方法名Reset是我这里定义的,也可以叫别的名字:free/release等等。

代码仓库里的实现是:识别带有refcount字段的结构体,然后遍历其Reset方法体语句。这个可以根据项目需求进行修改。

  • 识别结构体字段的类型定义
var refCountTypeName = map[string]uint8 {"refcount": 1}
  • 函数识别名称的定义

安装

按照文档操作即可。

git clone [email protected]:beckjiang/structreset.git

cd structreset

./install.sh

使用

检查项目testdata

structresetx -d ./analysis/passes/structreset/testdata
image.png

你可能感兴趣的:(Golang对象复用静态代码检查工具)