first timer here,
The first NOTE in SliceTricks suggests that there is a potential memory leak problem when cutting or deleting elements in a slice
Although this is not documented anywhere, checking the sources: 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 }
As you can see, storage for both the key (line #600) and the value (line #602) are cleared / zeroed.
This means if any of the key or value was a pointer, or if they were values of complex types containing pointers, they are zeroed and therefore the pointed objects are no longer referenced by the internal data structures of the map, so there is no memory leak here.
When there is no more reference to a complete map
value, then the complete memory area of the map
will be garbage collected, and all the pointers included in keys and values are also not held anymore by the map; and if no one else has reference to the pointed objects, they will be garbage collected properly.
We can also construct a test code which proves this without examining the sources:
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)
}
Output (try it on the Go Playground):
Put in map: 0x1040a128 &{X:1 Y:2}
Finalized: 0x1040a128 &{X:1 Y:2}
map[]
What does this do? It creates a *Point
value (pointer to a struct), puts it in the map, and registers a function that should be called when this pointer becomes unreachable (using runtime.SetFinalizer()), and then deletes the entry containing this pointer. Then we call runtime.GC() to "force" an immediate garbage collection. I also print the map at the end just to make sure the whole map is not garbage collected due to some optimization.
The result? We see the registered function gets called, which proves the pointer was removed from the map as the result of the delete()
call, because (since we had no other references to it) it was eligible for garbage collection.