dictionary - historial - En Go, ¿borrar una entrada de un mapa de punteros causa una pérdida de memoria?
borrar historial mapas ios 11 (2)
No, no habrá pérdidas de memoria al eliminar de un mapa.
En el caso de las divisiones, dado que una porción realmente usa una matriz subyacente, siempre que la división exista, incluso si usa solo una ranura en esa matriz, los elementos de puntero dentro de la matriz no pueden obtener la basura recolectada.
" Una porción describe una pieza de una matriz ", lo que implica que la matriz debe estar allí para que exista la división y GC no puede recopilarla; siempre y cuando algún código apunte a la porción.
primer temporizador aquí,
La primera NOTA en SliceTricks sugiere que existe un posible problema de pérdida de memoria al cortar o eliminar elementos en una porción de punteros.
Es lo mismo para un mapa? Por ejemplo: https://play.golang.org/p/67cN0JggWY
¿Deberíamos anular la entrada antes de eliminar del mapa? Al igual que:
m["foo"] = nil
¿Qué pasa si simplemente borramos el mapa?
m = make(map[string]*myStruct)
¿El recolector de basura todavía lo recogerá?
Gracias por adelantado
Verificando las fuentes
Aunque esto no está documentado en ninguna parte, verificando las fuentes: runtime/hashmap.go
, mapdelete()
function:
558 func mapdelete(t *maptype, h *hmap, key unsafe.Pointer) {
// ...
600 memclr(k, uintptr(t.keysize))
601 v := unsafe.Pointer(uintptr(unsafe.Pointer(b)) + dataOffset + bucketCnt*uintptr(t.keysize) + i*uintptr(t.valuesize))
602 memclr(v, uintptr(t.valuesize))
// ...
618 }
Como puede ver, el almacenamiento para la llave (línea n. ° 600) y el valor (línea n. ° 602) se borran / ponen a cero.
Esto significa que si alguna de las claves o valores era un puntero, o si eran valores de tipos complejos que contienen punteros, se ponen a cero y, por lo tanto, las estructuras de datos internas del mapa ya no hacen referencia a los objetos puntiagudos, por lo que no hay memoria gotear aquí.
Cuando ya no hay más referencia a un valor de map
completo, entonces el área de memoria completa del map
recogerá como basura, y todos los punteros incluidos en las claves y los valores tampoco se retienen en el mapa; y si nadie más tiene referencia a los objetos puntiagudos, serán basura recogida correctamente.
Construyendo un ejemplo para probar esto
También podemos construir un código de prueba que lo pruebe sin examinar las fuentes:
type point struct {
X, Y int
}
var m = map[int]*point{}
func main() {
fillMap()
delete(m, 1)
runtime.GC()
time.Sleep(time.Second)
fmt.Println(m)
}
func fillMap() {
p := &point{1, 2}
runtime.SetFinalizer(p, func(p *point) {
fmt.Printf("Finalized: %p %+v/n", p, p)
})
m[1] = p
fmt.Printf("Put in map: %p %+v/n", p, p)
}
Salida (pruébalo en el área de juegos Go ):
Put in map: 0x1040a128 &{X:1 Y:2}
Finalized: 0x1040a128 &{X:1 Y:2}
map[]
¿Qué hace esto? Crea un *Point
Valor de *Point
(puntero a una estructura), lo coloca en el mapa y registra una función que se debe runtime.SetFinalizer()
cuando este puntero se vuelve inalcanzable (usando runtime.SetFinalizer()
), y luego borra la entrada que contiene este puntero. Luego llamamos a runtime.GC()
para "forzar" una recolección de basura inmediata. También imprimo el mapa al final solo para asegurarme de que no se recoja el mapa completo debido a alguna optimización.
¿El resultado? Vemos que se llama a la función registrada, lo que demuestra que el puntero se eliminó del mapa como resultado de la llamada a delete()
porque (dado que no teníamos otras referencias) era elegible para la recolección de basura.