Gestion de la consommation de mémoire Go

je suis nouveau pour aller et essayer de comprendre comment il gère la consommation de mémoire.

j'ai des problèmes de mémoire dans un de mes projets de test. Je ne comprends pas pourquoi Go utilise de plus en plus de mémoire (ne la libérant jamais) quand mon programme tourne pendant longtemps.

j'exécute le cas d'essai fourni ci-dessous. Après la première allocation, le programme utilise près de 350 Mo de mémoire (selon ActivityMonitor). Puis j'essaie de le libérer et ActivityMonitor montre que la consommation de mémoire double. Pourquoi?

j'exécute ce code sur OS X en utilisant Go 1.0.3.

Qu'est-ce qui ne va pas avec ce code? Et quelle est la bonne façon de gérer les variables importantes dans les programmes Go?

j'ai eu un autre problème lié à la gestion de la mémoire lors de la mise en œuvre d'un algorithme qui utilise beaucoup de temps et de mémoire; après l'avoir exécuté pendant un certain temps, il jette une exception "out of memory".

package main

import ("fmt" 
"time"
)

func main() {
  fmt.Println("getting memory")
  tmp := make([]uint32, 100000000)
  for kk, _ := range tmp {
    tmp[kk] = 0
  }
  time.Sleep(5 * time.Second)
  fmt.Println("returning memory")
  tmp = make([]uint32, 1)
  tmp = nil
  time.Sleep(5 * time.Second)
  fmt.Println("getting memory")
  tmp = make([]uint32, 100000000)
  for kk, _ := range tmp {
    tmp[kk] = 0
  }
  time.Sleep(5 * time.Second)
  fmt.Println("returning memory")
  tmp = make([]uint32, 1)
  tmp = nil
  time.Sleep(5 * time.Second)  
  return
}
25
demandé sur Jeffrey Bosboom 2013-01-29 16:05:04
la source

2 ответов

Actuellement, utilise un marqueur et balayeur collecteur d'ordures, qui en général ne définit pas lorsque l'objet est jeté.

cependant, si vous regardez attentivement, il y a une routine de go appelée sysmon qui court essentiellement aussi longtemps que votre programme et appelle périodiquement le GC:

// forcegcperiod is the maximum time in nanoseconds between garbage
// collections. If we go this long without a garbage collection, one
// is forced to run.
//
// This is a variable for testing purposes. It normally doesn't change.
var forcegcperiod int64 = 2 * 60 * 1e9

(...)

// If a heap span goes unused for 5 minutes after a garbage collection,
// we hand it back to the operating system.
scavengelimit := int64(5 * 60 * 1e9)

forcegcperiod détermine la période après laquelle le GC est appelé de force. scavengelimit détermine le moment les travées sont renvoyées au système d'exploitation. les portées sont un certain nombre de pages de mémoire qui peut contenir plusieurs objets. Ils sont gardés pour scavengelimit temps et sont libérés si aucun objet n'est sur eux et scavengelimit est dépassé.

Plus bas dans le code, vous pouvez voir qu'il y a une option suivi. Vous pouvez l'utiliser pour voir, à chaque fois que l' le charognard pense qu'il doit nettoyer:

$ GOGCTRACE=1 go run gc.go
gc1(1): 0+0+0 ms 0 -> 0 MB 423 -> 350 (424-74) objects 0 handoff
gc2(1): 0+0+0 ms 1 -> 0 MB 2664 -> 1437 (2880-1443) objects 0 handoff
gc3(1): 0+0+0 ms 1 -> 0 MB 4117 -> 2213 (5712-3499) objects 0 handoff
gc4(1): 0+0+0 ms 2 -> 1 MB 3128 -> 2257 (6761-4504) objects 0 handoff
gc5(1): 0+0+0 ms 2 -> 0 MB 8892 -> 2531 (13734-11203) objects 0 handoff
gc6(1): 0+0+0 ms 1 -> 1 MB 8715 -> 2689 (20173-17484) objects 0 handoff
gc7(1): 0+0+0 ms 2 -> 1 MB 5231 -> 2406 (22878-20472) objects 0 handoff
gc1(1): 0+0+0 ms 0 -> 0 MB 172 -> 137 (173-36) objects 0 handoff
getting memory
gc2(1): 0+0+0 ms 381 -> 381 MB 203 -> 202 (248-46) objects 0 handoff
returning memory
getting memory
returning memory

comme vous pouvez le voir, aucun appel gc n'est fait entre la réception et le retour. Toutefois, si vous changer le délai de 5 secondes à 3 minutes (plus de 2 minutes à partir de forcegcperiod), les objets sont retirés par le gc:

returning memory
scvg0: inuse: 1, idle: 1, sys: 3, released: 0, consumed: 3 (MB)
scvg0: inuse: 381, idle: 0, sys: 382, released: 0, consumed: 382 (MB)
scvg1: inuse: 1, idle: 1, sys: 3, released: 0, consumed: 3 (MB)
scvg1: inuse: 381, idle: 0, sys: 382, released: 0, consumed: 382 (MB)
gc9(1): 1+0+0 ms 1 -> 1 MB 4485 -> 2562 (26531-23969) objects 0 handoff
gc10(1): 1+0+0 ms 1 -> 1 MB 2563 -> 2561 (26532-23971) objects 0 handoff
scvg2: GC forced // forcegc (2 minutes) exceeded
scvg2: inuse: 1, idle: 1, sys: 3, released: 0, consumed: 3 (MB)
gc3(1): 0+0+0 ms 381 -> 381 MB 206 -> 206 (252-46) objects 0 handoff
scvg2: GC forced
scvg2: inuse: 381, idle: 0, sys: 382, released: 0, consumed: 382 (MB)
getting memory

la mémoire n'est toujours pas libérée, mais le GC a marqué la région mémoire comme inutilisée. La libération commencera quand l'employée de l'échelle est utilisé et plus de limit. Du code de charognard:

if(s->unusedsince != 0 && (now - s->unusedsince) > limit) {
    // ...
    runtime·SysUnused((void*)(s->start << PageShift), s->npages << PageShift);
}

ce comportement peut bien sûr changer avec le temps, mais j'espère que vous avez maintenant un peu d'une sensation lorsque les objets sont jetés par la force, et quand ne pas.

comme le souligne zupa, libérer des objets peut ne pas rendre la mémoire au système d'exploitation, ainsi de suite certains systèmes peuvent ne pas changer l'utilisation de la mémoire. Cela semble être le cas pour Plan 9 et Windows selon ce fil sur les noix de golang.

35
répondu nemo 2017-11-29 12:20:08
la source

Pour finalement (la force) de recueillir la mémoire inutilisée, vous devez appeler runtime.GC().

variable = nil peut rendre les choses inaccessibles et donc éligibles pour la collecte, mais il en soi ne libère rien.

15
répondu zzzz 2013-01-29 16:14:23
la source

Autres questions sur