如何安全地在golang中回收protobuf对象。

3

我希望在运行时回收protobuf的消息对象来减少GC消耗,但不确定是否安全。以下是测试样例代码:

test.proto

message Hello{
  uint32  id   = 1;
}

test.pb.go

type Hello struct {
    state         protoimpl.MessageState
    sizeCache     protoimpl.SizeCache
    unknownFields protoimpl.UnknownFields

    ID  uint32 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"`
}

func (x *Hello) Reset() {
    *x = Hello{}
    if protoimpl.UnsafeEnabled {
        mi := &file_proto_login_api_login_proto_msgTypes[0]
        ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
        ms.StoreMessageInfo(mi)
    }
}
// other codes

main.go

func main() {
    // Disable GC to test re-acquire the same data
    gc := debug.SetGCPercent(-1)

    // As a protobuf object pool
    cache := sync.Pool{New: func() interface{} { return &test.Hello{} }}
    
    // Take out an object and use it
    m1 := cache.Get().(*test.Hello)
    m1.ID = 999
    fmt.Println(&m1.ID) // print 999

    // Empty the data and put it back into the object pool
    m1.Reset()
    cache.Put(m1)

    // Take out an object again and use it
    m2 := cache.Get().(*test.Hello)
    fmt.Println(&m2.ID) // print 0

    debug.SetGCPercent(gc)
}
1个回答

1
您展示的代码是安全的。当对象引用被放入池中后仍然被保留时,像这样的池化将变得“不安全”。您可能会面临竞态条件或奇怪的错误。因此,它还取决于使用您的对象的代码。
据我所知,协议缓冲库和gRPC库不会保留对protobuf对象的引用。这样做会破坏很多代码,因为这些库无法知道何时可以安全地重用。只要确保您自己的代码在将对象放入池中后不再使用它们,就应该没问题。

有一个问题。看一下行* x = Hello {},当调用Reset()函数时,似乎在函数内部创建了一个临时对象并分配给变量x。这不会改变数据的原始指针吗?谢谢。 - null
1
不,*x = Hello{}x 前面的星号表示指针被解引用了。这行代码基本上与将每个字段单独设置为其默认值相同。指针保持不变,只是它所指向的数据被更改了。 - Dylan Reimerink

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接