comment analyser golang mémoire

j'ai écrit un programme golang, il a coûté 1,2 Go à l'exécution. lorsque j'utilise

go tool pprof http://10.10.58.118:8601/debug/pprof/heap

obtenu une décharge。

il montre seulement 323,4 MB dans le tas. quel autre souvenir a disparu ?

y a-t-il un meilleur outil pour expliquer la mémoire d'exécution de golang ?


quand j'utilise gcvis j'ai obtenu ceci

enter image description here

<!-Et le profil de la forme de tas enter image description here

mon code

https://github.com/sharewind/push-server/blob/v3/broker
42
demandé sur sharewind 2014-07-21 14:43:04

4 réponses

le profil heap montre la mémoire active, mémoire que le runtime croit être utilisée par le programme go (c'est à dire: n'a pas été collecté par le collecteur d'ordures). Lorsque le GC recueille de la mémoire, le profil rétrécit, mais pas de mémoire est renvoyé dans le système. Vos attributions futures vont essayer d'utiliser la mémoire de la piscine des objets précédemment collectés avant de demander au système pour plus.

De l'extérieur, cela signifie que l'utilisation de la mémoire de votre programme va soit augmenter, ou rester au niveau. Ce que le système extérieur présente comme la "taille de résident" de votre programme est le nombre d'octets de RAM est assigné à votre programme, qu'il ait des valeurs go en cours d'utilisation ou collectées.

La raison pour laquelle ces deux nombres sont souvent très différents, parce que:

  1. Le GC de la collecte de la mémoire n'a pas d'effet sur la vue de l'extérieur du programme
  2. fragmentation de la mémoire
  3. le GC ne fonctionne que lorsque la mémoire utilisée est doublée la mémoire utilisée après le GC précédent (par défaut, voir:http://golang.org/pkg/runtime/#pkg-overview)

si vous voulez une analyse précise de la façon dont Go voit la mémoire, vous pouvez utiliser le runtime.ReadMemStats appel: http://golang.org/pkg/runtime/#ReadMemStats

alternativement, puisque vous utilisez le profilage basé sur le web si vous pouvez accéder aux données de profilage à travers votre navigateur à:http://10.10.58.118:8601/debug/pprof/ , en cliquant sur le lien tas vous verrez le vue de débogage du profil heap, qui a une impression d'un runtime.Structure MemStats en bas.

Le moteur d'exécution.MemStats documentation ( http://golang.org/pkg/runtime/#MemStats) A l'explication de tous les champs, mais les plus intéressants pour cette discussion sont:

  • HeapAlloc: essentiellement ce que le profileur vous donne (active heap memory)
  • Alloc: similaire à HeapAlloc, mais pour tous les go gérés de mémoire
  • Sys: la quantité totale de mémoire (espace d'adresse) demandée à L'OS

il y aura encore des divergences entre LA SAJR, et ce que l'OS rapporte, parce que ce que Go demande du système, et ce que L'OS lui donne ne sont pas toujours les mêmes. De plus, la mémoire CGO / syscall (par exemple: malloc / mmap) n'est pas suivie par go.

42
répondu Cookie of Nine 2014-07-21 12:50:30

comme un ajout à la réponse de @Cookie of Nine, en bref: vous pouvez essayer le --alloc_space option.

go tool pprof utiliser --inuse_space par défaut. Il échantillonne l'utilisation de la mémoire de sorte que le résultat est un sous-ensemble de réel.

Par --alloc_space pprof retourne toute la mémoire allouée depuis le début du programme.

18
répondu menghan 2014-07-22 08:47:54

j'étais toujours confus au sujet de la mémoire résidentielle croissante de mes applications de Go, et finalement j'ai dû apprendre les outils de profilage qui sont présents dans l'écosystème de Go. Runtime fournit de nombreux paramètres dans un runtime.Memstats la structure, mais il peut être difficile de comprendre laquelle d'entre elles peut aider à découvrir les raisons de la croissance de la mémoire, de sorte que certains outils supplémentaires sont nécessaires.

Profilage de l'environnement

Utilisation https://github.com/tevjef/go-runtime-metrics dans votre demande. Par exemple, vous pouvez mettre ceci dans votre 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
    }
}

Exécuter InfluxDB et Grafana à l'intérieur de Docker emballage:

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

configurer l'interaction entre Grafana et InfluxDBGrafana (page principale de Grafana - > coin supérieur gauche -> sources de données - > ajouter une nouvelle source de données):

enter image description here

importer tableau de bord #3242 de https://grafana.com (Grafana page principale - > coin supérieur gauche - > tableau de bord - > importation):

enter image description here

enfin, lancez votre application: elle transmettra les paramètres d'exécution auInfluxdb. Mettez votre demande sous une charge raisonnable (dans mon cas, il était tout à fait petit - 5 RPS pendant plusieurs heures).

analyse de la consommation de mémoire

  1. Sys (le synonim de RSS) de la courbe est assez similaire à HeapSys courbe. Il s'avère que l'allocation dynamique de la mémoire était le facteur principal de la croissance globale de la mémoire, de sorte que la petite quantité de mémoire consommée par les variables de pile semblent être constants et peuvent être ignorés;
  2. La quantité constante de goroutines garantit l'absence de goroutine des fuites et des variables de pile fuite;
  3. le montant total des objets alloués reste le même (il n'y a pas de raison de prendre en compte les fluctuations) pendant la durée de vie du procédé.
  4. le fait le plus surprenant:HeapIdle croît avec le même rythme qu'un Sys tandis que HeapReleased est toujours à zéro. De toute évidence, runtime ne renvoie pas la mémoire à OS tout, au moins dans les conditions de cet essai:
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.

enter image description hereenter image description here

Pour ceux qui est en train d'essayer d'étudier le problème de la consommation de mémoire, je recommande de suivre les étapes décrites afin d'exclure certaines erreurs triviales (comme la fuite de goroutine).

libérer la mémoire explicitement

il est intéressant que l'on puisse réduire significativement la consommation de mémoire avec des appels explicites à debug.FreeOSMemory():

// in the top-level package
func init() {
   go func() {
       t := time.Tick(time.Second)
       for {
           <-t
           debug.FreeOSMemory()
       }
   }()
}

comparison

en fait, cette approche a permis d'économiser environ 35% mémoire comparée aux conditions par défaut.

Voir aussi

https://github.com/golang/go/issues/14521

5
répondu Vitaly Isaev 2017-12-18 02:19:28

vous pouvez aussi utiliser StackImpact, qui enregistre et signale automatiquement les profils d'allocation de mémoire réguliers et déclenchés par des anomalies au tableau de bord, qui sont disponibles sous une forme historique et comparable. Voir ce billet de blog pour plus de détails Détection Fuite de Mémoire dans l'environnement de Production des Applications

enter image description here

Disclaimer: je travaille pour StackImpact

2
répondu logix 2016-10-14 13:21:04