memory management - Ir a la gestión del consumo de memoria
memory-management go (2)
Actualmente, go usa un recolector de elementos no utilizados , que en general no define cuándo se descarta el objeto.
Sin embargo, si observa detenidamente, existe una rutina llamada sysmon
que se ejecuta esencialmente siempre que su programa lo haga y llama al GC periódicamente:
// 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
determina el período después del cual se llama al GC por la fuerza. scavengelimit
determina cuándo se devuelven los intervalos al sistema operativo. Las extensiones son una cantidad de páginas de memoria que pueden contener varios objetos. Se guardan para el tiempo de scavengelimit
y se liberan si no hay ningún objeto sobre ellos y se excede scavengelimit
.
Más abajo en el código, puede ver que hay una opción de rastreo. Puede usar esto para ver, siempre que el carroñero cree que necesita limpiar:
$ 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
Como puede ver, no se realiza una invocación de gc entre get y return. Sin embargo, si cambia la demora de 5 segundos a 3 minutos (más de los 2 minutos de forcegcperiod
), el gc eliminará los objetos:
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 memoria aún no se ha liberado, pero el GC marcó la región de memoria como no utilizada. La liberación comenzará cuando el tramo utilizado no se use y sea anterior al limit
. Del código del carroñero:
if(s->unusedsince != 0 && (now - s->unusedsince) > limit) {
// ...
runtime·SysUnused((void*)(s->start << PageShift), s->npages << PageShift);
}
Este comportamiento puede, por supuesto, cambiar con el tiempo, pero espero que ahora se sienta un poco cuando los objetos se tiran por la fuerza y cuando no.
Como lo señala zupa, la liberación de objetos puede no devolver la memoria al sistema operativo, por lo que es posible que en ciertos sistemas no se produzca un cambio en el uso de la memoria. Este parece ser el caso para Plan 9 y Windows según este hilo en golang-nuts .
Soy nuevo en Ir e intento descubrir cómo maneja el consumo de memoria.
Tengo problemas con la memoria en uno de mis proyectos de prueba. No entiendo por qué Go usa más y más memoria (nunca la libera) cuando mi programa se ejecuta durante mucho tiempo.
Estoy ejecutando el caso de prueba a continuación. Después de la primera asignación, el programa usa casi 350 MB de memoria (según ActivityMonitor). Luego trato de liberarlo y ActivityMonitor muestra que el consumo de memoria se duplica. ¿Por qué?
Estoy ejecutando este código en OS X usando Go 1.0.3.
¿Qué está mal con este código? ¿Y cuál es la forma correcta de administrar grandes variables en los programas Go?
Tuve otro problema relacionado con la gestión de la memoria cuando implementé un algoritmo que usa mucho tiempo y memoria; después de ejecutarlo por un tiempo arroja una excepción de "memoria insuficiente".
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
}
Para eventualmente (forzar) recolectar la memoria no utilizada debe llamar a runtime.GC()
.
variable = nil
puede hacer que las cosas sean inalcanzables y, por lo tanto, elegibles para la recopilación, pero por sí mismo no libera nada.