Golang:内存分配和垃圾回收
分类:
文章
•
2024-03-22 09:38:40
堆和栈区别
- 空间分配区别:
- 栈:由操作系统(编译器)自动分配释放,存放函数的参数值,局部变量的值等,其操作方式类似于数据结构中的栈。
- 堆:一般由代码分配释放,若代码没有显式释放,程序结束时可能由OS回收,分配方式类似链表。
- 缓存方式区别:
- 栈:使用的是一级缓存,通常都是被调用时处于存储空间中,调用完毕立即释放。
- 堆:存放在二级缓存中,生命周期由垃圾回收算法来决定。
- 数据结构区别:
逃逸分析
-
逃逸分析是一种确定指针动态范围的方法,简单来说就是分析在程序的哪些地方可以访问到该指针。通俗的讲,逃逸分析就是确定一个变量要放堆上还是栈上。
规则如下:
- 是否在其他地方(非局部)被引用,只要有可能被引用了,那么一定分配到堆上,否则分配到栈上。
- 即使没有被外部引用,但对象过大,无法存放在栈区上,依然有可能分配到堆上。
- 逃逸分析是编译器用于决定变量分配到堆上还是栈上的行为,换句话说,
在编译阶段确定分配到哪里
。
-
栈逃逸
就是变量被分配到了堆上。尽可能在栈上分配内存,如果都分配到了堆上,会造成以下后果:
- 垃圾回收压力增大。
- 申请、分配、回收内存的系统开销增大。
- 动态分配产生一定量碎片问题。
Go命令分析
-
go build -gcflags "-m -l" analysis.go
,-gcflags表示有参数,参数-m会打印逃逸分析的优化策略,-l会禁用函数内联。
-
go tool compile -S analysis.go
,查看汇编代码。
垃圾回收
- Garbage Collection缩写为GC,是一种自动的存储器管理机制,当一个变量不再被引用时,就应该回收,让出存储空间。
- 简单来说,
垃圾回收(GC)是在后台运行一个守护线程,它的作用是在监控各个对象的状态,识别并且丢弃不再使用的对象来释放和重用资源
。
- go的垃圾回收使用
三色标记法
配合写屏障
和辅助GC
,三色标记法是标记清除法
的增强版本
标记清除法(mark and sweep)
- 标记:先STW(Stop The World),暂停整个程序的全部运行线程,将被引用的对象打上标记。
- 清除:清除没有被打标记的对象,即回收内存资源,然后恢复运行。
- 问题在于要通过STW保证GC期间标记对象的状态不能变化,整个程序都要暂停。
三色标记法
- 1、初始状态所有对象都是白色。
- 2、从root根出发扫描所有根对象(root区域主要是程序运行到当前时刻的栈和全局数据区域?a,b),将他们引用的对象标记为灰色(A,B)。
- 3、灰色对象变为黑色,此外,若灰色对象引用了其他对象,则将被引用的对象标记为灰色(B引用D)
- 4、遍历灰色对象列表,执行上一步,直到不存在灰色对象,此时白色对象即为垃圾,进行回收。
Go GC如何工作
- Golang GC的大部分处理是和用户代码并行的,GC期间用户代码可能会改变某些对象的状态,如何实现并行呢?
- 1、Mark:
- Mark Prepare:初始化GC任务,包括开启写屏障(write barrier)和辅助GC(mutator assist),统计root对象的任务数量等,这个过程需要
STW
。
- GC Drains:扫描所有root对象,包括全局指针和goroutine(G)栈上的指针,扫描对于G栈时需停止该G,将其加入标记队列(灰色队列),并循环处理灰色队列的对象,直到灰色对象为空,
该过程后台并行执行
。
- 2、Mark Termination:完成标记工作,需要重新扫描全局指针和栈,
因为Mark和用户程序是并行的,所以在该过程中可能会有新的对象分配和指针赋值,这个时候就需要通过写屏障记录下来
,重新扫描再检查一下。这个过程也是会STW
的。
- 3、Sweep:按照标记结果回收所有的白色对象,
该过程后台并行执行
。
- 4、Sweep Termination:对未扫描的范围进行扫描,只有上一轮GC的清扫工作完成才可以开始新的一轮GC。
写屏障
- 每一轮GC开始时初始化一个
屏障
,记录第一次扫描时各个对象的状态,以便和第二次扫描对比。引用状态变化的对象标记为灰色。前后未变化的再正常处理。
辅助GC
- Golang实际是把
用户代码-->大段GC--->用户代码
分散为用户代码--->小段GC--->用户代码--->小段GC--->用户代码
,如果GC速度跟不上分配速度,会把用户逻辑暂停,同时会把用户线程抢占过来加入到垃圾回收里面加快垃圾回收速度。
GC触发条件
- 1、超过内存大小阈值。
- 2、达到定时时间,默认2分钟。