这里不讲垃圾回收机制。
堆和栈的简单说明:
我们都知道变量占有内存,内存在底层分配上有堆和栈。
值类型变量的内存通常是在栈中分配
引用类型变量的内存通常在堆中分配
注意这里说的是"通常",因为变量又分为局部变量和全局变量。
我们来看一段代码:
// 编译器会自动选择在栈上还是在堆上分配局部变量的存储空间
// 但是让人惊讶的是,这个选择不由var和new这两个关键字决定var global *intfunc f() {var x intx = 1global = &x
}func g() {y := new(int)*y = 1
}
函数里的x变量必须在堆上分配,因为它在函数退出后依然可以通过包一级的global变量找到,虽然它是在函数内部定义的;用Go语言的术语说,这个x局部变量从函数f中逃逸了。相反,当g函数返回时,变量*y将是不可达的,也就是说可以马上被回收的。因此,y并没有从函数g中逃逸,编译器可以选择在栈上分配y的存储空间(译注:也可以选择在堆上分配,然后由Go语言的GC回收这个变量的内存空间),虽然这里用的是new方式。其实在任何时候,你并不需为了编写正确的代码而要考虑变量的逃逸行为,要记住的是,逃逸的变量需要额外分配内存,同时对性能的优化可能会产生细微的影响。
// size of last memory has been freed
var lastTotalFreed uint64func InitMap(a map[int]int) {for i := 0; i < 100000; i++ {a[i] = i}
}func ClearMap(a map[int]int) {for i := 0; i < 100000; i++ {delete(a, i)}
}var a map[int]intfunc main() {a = make(map[int]int)printMemStats()InitMap(a)runtime.GC()printMemStats()ClearMap(a)runtime.GC()printMemStats()a = nilruntime.GC()printMemStats()
}func printMemStats() {var m runtime.MemStatsruntime.ReadMemStats(&m)log.Printf("Alloc = %v TotalAlloc = %v Just Freed = %v Sys = %v NumGC = %v\n",m.Alloc/1024, m.TotalAlloc, ((m.TotalAlloc-m.Alloc)-lastTotalFreed)/1024,m.Sys/1024, m.NumGC)lastTotalFreed = m.TotalAlloc - m.Alloc
}
结果如下:
2023/02/08 13:25:40 Alloc = 146 TotalAlloc = 150240 Just Freed = 0 Sys = 6292 Nu
mGC = 0
2023/02/08 13:25:40 Alloc = 2847 TotalAlloc = 5981008 Just Freed = 2993 Sys = 11
480 NumGC = 2
2023/02/08 13:25:40 Alloc = 2848 TotalAlloc = 5982816 Just Freed = 0 Sys = 11480NumGC = 3
2023/02/08 13:25:40 Alloc = 145 TotalAlloc = 5986400 Just Freed = 2706 Sys = 114
80 NumGC = 4
实验结论:
引用类型的全局变量的内存被垃圾回收,先不下结论引用类型的全局变量的内存分配在栈上还是堆上
把a改成局部变量:
// size of last memory has been freed
var lastTotalFreed uint64func InitMap(a map[int]int) {for i := 0; i < 100000; i++ {a[i] = i}
}func ClearMap(a map[int]int) {for i := 0; i < 100000; i++ {delete(a, i)}
}func main() {a := make(map[int]int)printMemStats()InitMap(a)runtime.GC()printMemStats()ClearMap(a)runtime.GC()printMemStats()a = nilruntime.GC()printMemStats()
}func printMemStats() {var m runtime.MemStatsruntime.ReadMemStats(&m)log.Printf("Alloc = %v TotalAlloc = %v Just Freed = %v Sys = %v NumGC = %v\n",m.Alloc/1024, m.TotalAlloc, ((m.TotalAlloc-m.Alloc)-lastTotalFreed)/1024,m.Sys/1024, m.NumGC)lastTotalFreed = m.TotalAlloc - m.Alloc
}
我们来看实验结果:
2023/02/08 13:29:38 Alloc = 146 TotalAlloc = 149616 Just Freed = 0 Sys = 6292 Nu
mGC = 0
2023/02/08 13:29:38 Alloc = 2843 TotalAlloc = 5961632 Just Freed = 2978 Sys = 11
416 NumGC = 2
2023/02/08 13:29:38 Alloc = 144 TotalAlloc = 5965240 Just Freed = 2702 Sys = 114
16 NumGC = 3
2023/02/08 13:29:38 Alloc = 146 TotalAlloc = 5968720 Just Freed = 1 Sys = 11416
NumGC = 4
实验结论:
引用类型的局局变量的内存没被垃圾回收,先不下结论引用类型的局局变量的内存分配在栈上还是堆上。
var lastTotalFreed uint64 // size of last memory has been freedfunc InitMap(a map[int]int) {for i := 0; i < 100000; i++ {a[i] = i}
}
func ClearMap(a map[int]int) {for i := 0; i < 100000; i++ {delete(a, i)}
}var global *map[int]intfunc main() {a := make(map[int]int)printMemStats()global = &aInitMap(a)runtime.GC()printMemStats()ClearMap(a)runtime.GC()printMemStats()a = nilruntime.GC()printMemStats()}
func printMemStats() {var m runtime.MemStatsruntime.ReadMemStats(&m)log.Printf("Alloc = %v TotalAlloc = %v Just Freed = %v Sys = %v NumGC = %v\n",m.Alloc/1024, m.TotalAlloc/1024, ((m.TotalAlloc-m.Alloc)-lastTotalFreed)/1024, m.Sys/1024, m.NumGC)lastTotalFreed = m.TotalAlloc - m.Alloc
}
实验结果如下:
2023/02/08 13:33:02 Alloc = 146 TotalAlloc = 146 Just Freed = 0 Sys = 6292 NumG
C = 0
2023/02/08 13:33:02 Alloc = 2859 TotalAlloc = 5851 Just Freed = 2991 Sys = 1141
6 NumGC = 2
2023/02/08 13:33:02 Alloc = 2863 TotalAlloc = 5854 Just Freed = 0 Sys = 11416 N
umGC = 3
2023/02/08 13:33:02 Alloc = 145 TotalAlloc = 5856 Just Freed = 2719 Sys = 11416NumGC = 4
由于堆局部变量做了处理,使其确定在堆上分配,就被回收了,由此证明GO的GC只回收堆上的内存。
上一篇:引导滤波code
下一篇:Array 对象方法(2)