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
<!-Et le profil de la forme de tasmon code
https://github.com/sharewind/push-server/blob/v3/broker
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:
- Le GC de la collecte de la mémoire n'a pas d'effet sur la vue de l'extérieur du programme
- fragmentation de la mémoire
- 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.
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.
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 InfluxDB
Grafana
(page principale de Grafana - > coin supérieur gauche -> sources de données - > ajouter une nouvelle source de données):
importer tableau de bord #3242 de https://grafana.com (Grafana page principale - > coin supérieur gauche - > tableau de bord - > importation):
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
Sys
(le synonim deRSS
) 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;- La quantité constante de goroutines garantit l'absence de goroutine des fuites et des variables de pile fuite;
- 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é.
- le fait le plus surprenant:
HeapIdle
croît avec le même rythme qu'unSys
tandis queHeapReleased
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.
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()
}
}()
}
en fait, cette approche a permis d'économiser environ 35% mémoire comparée aux conditions par défaut.
Voir aussi
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
Disclaimer: je travaille pour StackImpact