ero sempre confuso circa il crescente di memoria residenziale di mie applicazioni Go, e alla fine ho dovuto imparare gli strumenti di profiling che sono presenti nell'ecosistema Go. Runtime fornisce molte metriche all'interno di una struttura runtime.Memstats, ma può essere difficile da capire quali di esse può aiutare a scoprire le ragioni della crescita di memoria, quindi sono necessari alcuni strumenti aggiuntivi.
Profiling ambiente
Uso https://github.com/tevjef/go-runtime-metrics nell'applicazione. Per esempio, si può mettere questo nel vostro main
:
import(
metrics "github.com/tevjef/go-runtime-metrics"
)
func main() {
//...
metrics.DefaultConfig.CollectionInterval = time.Second
if err := metrics.RunCollector(metrics.DefaultConfig); err != nil {
// handle error
}
}
Run InfluxDB
e Grafana
entro Docker
contenitori:
docker run --name influxdb -d -p 8086:8086 influxdb
docker run -d -p 9090:3000/tcp --link influxdb --name=grafana grafana/grafana:4.1.0
Impostare interazione tra Grafana
e InfluxDB
Grafana
(pagina principale Grafana -> angolo alto a sinistra -> Origini dati -> Aggiungi nuova origine dati:
Importa cruscotto #3242 da https://grafana.com (pagina principale Grafana -> Top sinistro -> Dashboard -> Importa):
Infine, lanciare la vostra applicazione: trasmetterà metriche di runtime al contenerized Influxdb
.Metti la tua applicazione sotto un carico ragionevole (nel mio caso era piuttosto piccola - 5 RPS per diverse ore).
consumo di memoria analisi
Sys
(il sinonimo di RSS
) curva è molto simile a HeapSys
curva. Risulta che l'allocazione dinamica della memoria era il fattore principale della crescita complessiva della memoria, quindi la piccola quantità di memoria consumata dalle variabili dello stack sembra essere costante e può essere ignorata;
- La quantità costante di goroutine garantisce l'assenza di perdite di goroutine/perdita di variabili dello stack;
- La quantità totale di oggetti allocati rimane la stessa (non è necessario tenere conto delle fluttuazioni) durante la vita del processo.
- Il fatto più sorprendente:
HeapIdle
cresce con la stessa velocità di Sys
, mentre HeapReleased
è sempre zero. Ovviamente runtime non restituisce la memoria di OS affatto, almeno nelle condizioni di questo test:
HeapIdle minus HeapReleased estimates the amount of memory
that could be returned to the OS, but is being retained by
the runtime so it can grow the heap without requesting more
memory from the OS.
Per chi sta cercando di studiare il problema del consumo di memoria raccomando di seguire i passaggi descritti per escludere alcuni errori banali (come la perdita di goroutine).
memoria Liberare esplicitamente
E 'interessante il fatto che l'uno può ridurre in modo significativo il consumo di memoria con le chiamate esplicite a debug.FreeOSMemory()
:
// in the top-level package
func init() {
go func() {
t := time.Tick(time.Second)
for {
<-t
debug.FreeOSMemory()
}
}()
}
In realtà, questo approccio ha salvato circa il 35% di memoria rispetto alle condizioni predefinite.
Vedi anche
https://github.com/golang/go/issues/14521
Pubblica il tuo codice. Dicci cosa fa il tuo programma. – elithrar
Forse a causa di un gc? http://dave.cheney.net/2014/07/11/visualising-the-go-garbage-collector potrebbe aiutare. – VonC
Sembra che la memoria rimanente non sia garbaged raccolta e rilasciata al sistema. È fatto dopo pochi minuti di inattività. Attendere 8 minuti e ricontrollare. Controlla questo link per una guida su come eseguire il debug/profilo Vai programmi: https://software.intel.com/en-us/blogs/2014/05/10/debugging-performance-issues-in-go-programs – siritinga