9

runtime.SetFinalizer有没有办法获得使用注册但尚未运行的终结器总数?

我们正在考虑struct在我们的一些产品中添加带有注册终结器的 a 以释放使用 分配的内存malloc,并且该对象可能具有相对较高的分配率。如果我们可以监控终结器的数量,以确保它们不会堆积并触发内存不足错误(就像其他垃圾收集器一样),那就太好了。

(我知道显式释放可以避免这个问题,但我们不能更改现有代码,它不会调用Close函数或类似的东西。)

4

2 回答 2

4

您可以通过在创建和完成新对象时分别递增和递减未导出的包变量来保持对这些对象的计数。

例如:

package main

import (
    "fmt"
    "runtime"
    "sync/atomic"
)

var totalObjects int32

func TotalObjects() int32 {
    return atomic.LoadInt32(&totalObjects)
}

type Object struct {
    p uintptr // C allocated pointer
}

func NewObject() *Object {
    o := &Object{
    }
    // TODO: perform other initializations
    atomic.AddInt32(&totalObjects, 1)
    runtime.SetFinalizer(o, (*Object).finalizer)
    return o
}

func (o *Object) finalizer() {
    atomic.AddInt32(&totalObjects, -1)
    // TODO: perform finalizations
}

func main() {
    fmt.Println("Total objects:", TotalObjects())
    for i := 0; i < 100; i++ {
        _ = NewObject()
        runtime.GC()
    }
    fmt.Println("Total objects:", TotalObjects())
}

https://play.golang.org/p/n35QABBICj

于 2017-12-03T17:53:12.640 回答
0

可以制作一个包装器runtime.SetFinalizer来为您计算。当然,这是在您使用的任何地方都使用它的问题SetFinalizer

如果这有问题,您也可以SetFinalizer直接修改源代码,但这需要修改后的 Go 编译器

原子整数SetFinalizer可以在不同的线程上调用,否则计数器可能不准确,因为没有这些可能会发生竞争条件。Golang 保证终结器是从单个 goroutine 调用的,因此内部函数不需要它。

https://play.golang.org/p/KKCH2UwTFYw

package main

import (
    "fmt"
    "reflect"
    "runtime"
    "sync/atomic"
)

var finalizersCreated int64
var finalizersRan int64

func SetFinalizer(obj interface{}, finalizer interface{}) {
    finType := reflect.TypeOf(finalizer)
    funcType := reflect.FuncOf([]reflect.Type{finType.In(0)}, nil, false)
    f := reflect.MakeFunc(funcType, func(args []reflect.Value) []reflect.Value {
        finalizersRan++
        return reflect.ValueOf(finalizer).Call([]reflect.Value{args[0]})
    })
    runtime.SetFinalizer(obj, f.Interface())
    atomic.AddInt64(&finalizersCreated, 1)
}

func main() {
    v := "a"
    SetFinalizer(&v, func(a *string) {
        fmt.Println("Finalizer ran")
    })
    fmt.Println(finalizersRan, finalizersCreated)
    runtime.GC()
    fmt.Println(finalizersRan, finalizersCreated)
}
于 2018-02-24T09:34:03.433 回答