2017-12-03 3 views
9

Gibt es eine Möglichkeit, die Gesamtzahl der Finalizer zu erhalten, die mit runtime.SetFinalizer registriert wurden und die noch nicht ausgeführt wurden?Finalizer-Statistik

Wir erwägen die Hinzufügung eines struct mit einem registrierten Finalizer zu einigen unserer Produkte, um Speicher freizugeben, der unter Verwendung malloc zugewiesen wurde, und das Objekt möglicherweise eine relativ hohe Zuordnungsrate haben kann. Es wäre schön, wenn wir die Anzahl der Finalizer überwachen könnten, um sicherzustellen, dass sie sich nicht häufen und Speicherfehler verursachen (wie sie es bei anderen Garbage Collectors tun).

(Ich bin mir bewusst, dass explizite Freigabe dieses Problem vermeiden würde, aber wir können den vorhandenen Code nicht ändern, die keine Close Funktion oder so etwas nicht nennen.)

Antwort

4

können Sie halten eine Anzahl von ihnen halten Objekte durch Inkrementieren und Dekrementieren einer nicht-exportierten Paketvariable, wenn ein neues Objekt erstellt bzw. finalisiert wird.

Zum Beispiel:

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

+0

Ich glaube, ich muss dies in der Laufzeit selbst setzen würde, weil ich nicht alle beteiligten Quellen patchen können. Es ist jedoch möglich, Finalizer zu entfernen, so dass das Zählen nicht trivial wird. Ich hatte auf eine Möglichkeit gehofft, Zähler aus der Laufzeit selbst zu sammeln. –

0

Es ist möglich, einen Wrapper auf runtime.SetFinalizer zu machen, die das Zählen für Sie tun. Natürlich ist es eine Frage der Verwendung überall wo Sie SetFinalizer verwenden.

Falls dies problematisch ist, können Sie auch SetFinalizer Quellcode direkt ändern, aber das erfordert a modified Go compiler.

Atomare Integer werden verwendet, da an verschiedenen Threads aufgerufen werden kann und ansonsten ein Zähler möglicherweise nicht genau ist, da ohne diese möglicherweise eine Race-Bedingung auftreten könnte. Golang garantiert, dass Finalizer von einer einzigen Goroutine aufgerufen werden, so dass sie für die innere Funktion nicht benötigt werden.

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