Go的GC回收的堆还是栈,我们用实验来说明
创始人
2024-05-24 22:21:37
0

这里不讲垃圾回收机制。

先给出三个结论

  1. Go的垃圾回收是针对堆的(所有的垃圾回收都是针对堆的,这里只是做一个证明)
  2. 引用类型的全局变量内存分配在堆上,值类型的全局变量分配在栈上。
  3. 局部变量内存分配可能在栈上也可能在堆上。

堆和栈的简单说明

  1. 栈(操作系统):由操作系统自动分配释放
  2. 堆(操作系统): 一般由程序员分配释放,例如在c/c++中,在golang,java,python有自动的垃圾回收机制

我们都知道变量占有内存,内存在底层分配上有堆和栈。

  • 值类型变量的内存通常是在栈中分配

  • 引用类型变量的内存通常在堆中分配

注意这里说的是"通常",因为变量又分为局部变量和全局变量。

  • 当变量是全局变量时,符合上面所说的分配规则
  • 但当变量是局部变量时,分配在堆和栈就变的不确定了

我们来看一段代码:

// 编译器会自动选择在栈上还是在堆上分配局部变量的存储空间
// 但是让人惊讶的是,这个选择不由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)

相关内容

热门资讯

喜欢穿一身黑的男生性格(喜欢穿... 今天百科达人给各位分享喜欢穿一身黑的男生性格的知识,其中也会对喜欢穿一身黑衣服的男人人好相处吗进行解...
发春是什么意思(思春和发春是什... 本篇文章极速百科给大家谈谈发春是什么意思,以及思春和发春是什么意思对应的知识点,希望对各位有所帮助,...
网络用语zl是什么意思(zl是... 今天给各位分享网络用语zl是什么意思的知识,其中也会对zl是啥意思是什么网络用语进行解释,如果能碰巧...
为什么酷狗音乐自己唱的歌不能下... 本篇文章极速百科小编给大家谈谈为什么酷狗音乐自己唱的歌不能下载到本地?,以及为什么酷狗下载的歌曲不是...
华为下载未安装的文件去哪找(华... 今天百科达人给各位分享华为下载未安装的文件去哪找的知识,其中也会对华为下载未安装的文件去哪找到进行解...
怎么往应用助手里添加应用(应用... 今天百科达人给各位分享怎么往应用助手里添加应用的知识,其中也会对应用助手怎么添加微信进行解释,如果能...
家里可以做假山养金鱼吗(假山能... 今天百科达人给各位分享家里可以做假山养金鱼吗的知识,其中也会对假山能放鱼缸里吗进行解释,如果能碰巧解...
四分五裂是什么生肖什么动物(四... 本篇文章极速百科小编给大家谈谈四分五裂是什么生肖什么动物,以及四分五裂打一生肖是什么对应的知识点,希...
一帆风顺二龙腾飞三阳开泰祝福语... 本篇文章极速百科给大家谈谈一帆风顺二龙腾飞三阳开泰祝福语,以及一帆风顺二龙腾飞三阳开泰祝福语结婚对应...
美团联名卡审核成功待激活(美团... 今天百科达人给各位分享美团联名卡审核成功待激活的知识,其中也会对美团联名卡审核未通过进行解释,如果能...