面试官:map删除元素会释放内存吗

1,733 阅读1分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 1 天,点击查看活动详情

最初的map在设计时没有delete函数,想要删除元素,赋值false。具体可以查看以下三个历史issue。
github.com/golang/go/i…

github.com/golang/go/i…

github.com/golang/go/i…
相关语法:

 m[k] = nil, false

go1.0提供内置函数delete,可以从map中删除一组键值对,即使key不存在也保证删除操作的安全性。具体介绍:go.dev/blog/maps

但是内存并没有被释放,只是修改了一个标记,底层数组还是被占用着。

源码位置:github.com/golang/go/b…

同时,根据key类型不同,会优化成更具体的删除函数。mapdelete_fast32/mapdelete_fast64/mapdelete_faststr (runtime/map)
相关代码测试:

var intMap map[int]int
var cnt = 8192

func main() {
   //  alloc = 123 TotalAlloc = 123 Sys = 8067 NumGC = 0
   printMemStats()

   initMap()
   runtime.GC()
   // alloc = 428 TotalAlloc = 443 Sys = 8706 NumGC = 1
   printMemStats()
   // 8192
   log.Println(len(intMap))
   for i := 0; i < cnt; i++ {
      delete(intMap, i)
   }
   // 0
   log.Println(len(intMap))

   runtime.GC()
   // alloc = 430 TotalAlloc = 446 Sys = 8962 NumGC = 2
   printMemStats()

   intMap = nil
   //  alloc = 431 TotalAlloc = 447 Sys = 8706 NumGC = 2
   printMemStats()
   runtime.GC()
   //  alloc = 120 TotalAlloc = 450 Sys = 9218 NumGC = 3
   printMemStats()
}
func printMemStats() {
   var m runtime.MemStats
   runtime.ReadMemStats(&m)
   log.Printf("alloc = %v TotalAlloc = %v Sys = %v NumGC = %v\n", m.Alloc/1024, m.TotalAlloc/1024, m.Sys/1024, m.NumGC)
}
func initMap() {
   intMap = make(map[int]int, cnt)
   for i := 0; i < cnt; i++ {
      intMap[i] = i
   }
   
}
  • Alloc 当前堆上对象占用的内存大小 KB
  • TotalAlloc 堆上总共分配的内存大小
  • Sys 程序从操作系统总共申请的内存大小 KB
  • NumGC 垃圾回收的次数

调用delete进行删除元素时,虽然map的长度返回为0,但当前堆上对象占用的内存大小并没有改变。
但是将map置为nil后,进行gc,会发现当前堆上对象占用的内存大小变小。

同时,对于delete()没有返回值,但是对于sync.Map会返回bool值检查存在性。因为对于map来说,调用者通常知道元素是否存在,或者根本就不关心。

image.png

参考

github.com/golang/go/i…

blog.cyeam.com/json/2017/1…

github.com/talkgo/nigh…

www.bilibili.com/video/BV1pt…

总结

map删除元素并不会释放内存,只是修改标记。如果想要释放内存,可以将map设置为nil后调用gc来实现。