终结器统计

9
有没有一种方法可以获取使用runtime.SetFinalizer注册的总终结器数量,且这些终结器尚未运行?
我们正在考虑向我们的某些产品中添加带有注册终结器的struct,以释放使用malloc分配的内存,该对象可能具有相对较高的分配速率。如果我们能够监视终结器的数量,就可以确保它们不会堆积并触发内存不足错误(就像其他垃圾收集器一样)。
(我知道显式释放可以避免此问题,但我们无法更改现有代码,该代码不调用Close函数或类似函数。)
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


我认为我需要将其放入运行时本身,因为我无法修补涉及的所有源代码。但是可以取消注册终结器,使计数变得非平凡。我曾希望从运行时本身收集计数器的某种方法。 - Florian Weimer

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)
}

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