go version go1.13.1 darwin/amd64
尝试做一次实验:
package main
import (
"fmt"
"runtime"
)
//var a = make(map[int]struct{})
func main() {
v := struct{}{}
a := make(map[int]struct{})
for i := 0; i < 10000; i++ {
a[i] = v
}
runtime.GC()
printMemStats("添加1万个键值对后")
fmt.Println("删除前Map长度:", len(a))
for i := 0; i < 10000-1; i++ {
delete(a, i)
}
fmt.Println("删除后Map长度:", len(a))
// 再次进行手动GC回收
runtime.GC()
printMemStats("删除1万个键值对后")
// 设置为nil进行回收
a = nil
runtime.GC()
printMemStats("设置为nil后")
}
func printMemStats(mag string) {
var m runtime.MemStats
runtime.ReadMemStats(&m)
fmt.Printf("%v:分配的内存 = %vKB, GC的次数 = %v\n", mag, m.Alloc/1024, m.NumGC)
}
输出:
添加1万个键值对后:分配的内存 = 241KB, GC的次数 = 1
删除前Map长度: 10000
删除后Map长度: 1
删除1万个键值对后:分配的内存 = 65KB, GC的次数 = 2
设置为nil后:分配的内存 = 65KB, GC的次数 = 3
针对:底层内存不会真正的释放, 这样可能会导致内存一直增长下去造成问题。
可以看到,新版本的 Golang 难道真的会回收 map 的多余空间哦,难道哈希表会随着 map 里面的元素变少,然后缩小了?
我又尝试了一下,将 map 放在外层:
package main
import (
"fmt"
"runtime"
)
var a = make(map[int]struct{})
func main() {
v := struct{}{}
//a := make(map[int]struct{})
for i := 0; i < 10000; i++ {
a[i] = v
}
runtime.GC()
printMemStats("添加1万个键值对后")
fmt.Println("删除前Map长度:", len(a))
for i := 0; i < 10000-1; i++ {
delete(a, i)
}
fmt.Println("删除后Map长度:", len(a))
// 再次进行手动GC回收
runtime.GC()
printMemStats("删除1万个键值对后")
// 设置为nil进行回收
a = nil
runtime.GC()
printMemStats("设置为nil后")
}
func printMemStats(mag string) {
var m runtime.MemStats
runtime.ReadMemStats(&m)
fmt.Printf("%v:分配的内存 = %vKB, GC的次数 = %v\n", mag, m.Alloc/1024, m.NumGC)
}
输出:
添加1万个键值对后:分配的内存 = 243KB, GC的次数 = 1
删除前Map长度: 10000
删除后Map长度: 1
删除1万个键值对后:分配的内存 = 244KB, GC的次数 = 2
设置为nil后:分配的内存 = 67KB, GC的次数 = 3
这时 map 好像内存没变化,直到设置为 nil。
为什么全局变量就会不变呢?
而为什么,局部变量还在使用着,它里面还剩一个元素,为什么就会缩小呢,大家都是 map,空间会一直增长,局部变量有优先权变小?难道 Golang 底层做了一些特殊处理?
于是我又做了一次操作,将局部变量添加一万个数,然后再删除9999个数,再添加9999个,看其变化:
package main
import (
"fmt"
"runtime"
)
//var a = make(map[int]struct{})
func main() {
v := struct{}{}
a := make(map[int]struct{})
for i := 0; i < 10000; i++ {
a[i] = v
}
runtime.GC()
printMemStats("添加1万个键值对后")
fmt.Println("删除前Map长度:", len(a))
for i := 0; i < 10000-1; i++ {
delete(a, i)
}
fmt.Println("删除后Map长度:", len(a))
// 再次进行手动GC回收
runtime.GC()
printMemStats("删除1万个键值对后")
for i := 0; i < 10000-1; i++ {
a[i] = v
}
// 再次进行手动GC回收
runtime.GC()
printMemStats("再一次添加1万个键值对后")
// 设置为nil进行回收
a = nil
runtime.GC()
printMemStats("设置为nil后")
}
func printMemStats(mag string) {
var m runtime.MemStats
runtime.ReadMemStats(&m)
fmt.Printf("%v:分配的内存 = %vKB, GC的次数 = %v\n", mag, m.Alloc/1024, m.NumGC)
}
输出:
添加1万个键值对后:分配的内存 = 242KB, GC的次数 = 1
删除前Map长度: 10000
删除后Map长度: 1
删除1万个键值对后:分配的内存 = 243KB, GC的次数 = 2
再一次添加1万个键值对后:分配的内存 = 65KB, GC的次数 = 3
设置为nil后:分配的内存 = 65KB, GC的次数 = 4
这次局部变量删除后,和全局变量map一样了,内存耶没变化。
但是添加10000个数后内存反而变小了。
这神奇的 Golang 啊。
map删除元素后map内存是不会释放的,无论是局部还是全局,但引出了上面一个奇怪的问题。
map aa = nil