Golang:内存分配和垃圾回收

堆和栈区别

  • 空间分配区别:
    • 栈:由操作系统(编译器)自动分配释放,存放函数的参数值,局部变量的值等,其操作方式类似于数据结构中的栈。
    • 堆:一般由代码分配释放,若代码没有显式释放,程序结束时可能由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)。
    Golang:内存分配和垃圾回收
  • 3、灰色对象变为黑色,此外,若灰色对象引用了其他对象,则将被引用的对象标记为灰色(B引用D)
  • 4、遍历灰色对象列表,执行上一步,直到不存在灰色对象,此时白色对象即为垃圾,进行回收。
    Golang:内存分配和垃圾回收

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分钟。