首页  

GO gc 要点     所属分类 go 浏览量 218
Go1.12开始,使用 非分代的、并发的、基于三色标记清除的垃圾回收器 
Go是一种静态类型的编译型语言,Go不需要VM,
Go应用程序二进制文件中嵌入了一个小型运行时(Go runtime),
可以处理诸如垃圾收集(GC),调度和并发之类的语言功能

Go语言span内存池 tcmalloc分配机制 对象可以分配在栈上 对象池
span内存池  和 tcmalloc 内存分级 减少内存碎片

G  M P
G Goroutine 执行上下文环境
M 操作系统线程
P Processer

内存分配器采用 TCMalloc的设计思想

Page
Span
mcache   提供给P(逻辑处理器)的高速缓存,用于存储小对象(对象大小<= 32Kb)
mcentral 所有线程共享的缓存
mheap
栈

Tiny对象指大小在1Byte到16Byte之间并且不包含指针的对象

​核心思想
把内存分为多级管理,降低锁的粒度(只是去mcentral和mheap会申请锁), 
以及多种对象大小类型,减少分配产生的内存碎片

微小对象(Tiny)(size <16B)
  使用mcache的微小分配器分配小于16个字节的对象,并且在单个16字节块上可完成多个微小分配。
小对象(尺寸16B〜32KB)
  大小在16个字节和32k字节之间的对象被分配在G运行所在的P的mcache的对应的mspan size class上。
大对象(大小> 32KB)
  大于32 KB的对象直接分配在mheap的相应大小类上(size class)。如果mheap为空或没有足够大的页面满足分配请求,则它将从操作系统中分配一组新的页(至少1MB)
如果对应的大小规格在 mcache 中没有可用的块,则向 mcentral 申请
如果 mcentral 中没有可用的块,则向 mheap 申请,并根据 BestFit 算法找到最合适的 mspan
如果申请到的 mspan 超出申请大小,将会根据需求进行切分,以返回用户所需的页数。
剩余的页构成一个新的 mspan 放回 mheap 的空闲列表。

如果 mheap 中没有可用 span,则向操作系统申请一系列新的页(最小 1MB)
Go 会在操作系统分配超大的页(称作 arena)
分配一大批页会减少和操作系统通信的成本


​go内存 分成堆区(Heap)和 栈区(Stack)
栈区的内存由编译器自动进行分配和释放
Go堆是Go垃圾收集器管理的主要区域

标记清除算法

从根结点开始遍历,执行可达性分析算法,递归标记所有被引用的对象为存活状态
标记阶段结束后,垃圾收集器会依次遍历堆中的对象并清除其中的未被标记为存活的对象

GC Roots   全局变量 和 G Stack中的引用指针


三色可达性分析
按 是否被访问过 将程序中的对象分成白色 黑色 和 灰色

白色对象 
 对象尚未被垃圾收集器访问过,在可达性分析刚开始的阶段,所有的对象都是白色的,
 若在分析结束阶段,仍然是白色的对象,即代表不可达
 
黑色对象 
  表示对象已经被垃圾收集器访问过,且这个对象的所有引用都已经被扫描过,
  黑色的对象代表已经被扫描过而且是安全存活的,
  如果有其他对象只想黑色对象无需再扫描一遍,
  黑色对象不可能直接(不经过灰色对象)指向某个白色对象
  
灰色对象 
  表示对象已经被垃圾收集器访问过,但是这个对象上至少存在一个引用还没有被扫描过,
  因为存在指向白色对象的外部指针,垃圾收集器会扫描这些对象的子对象


三色可达性分析算法大致流程

初始状态所有对象都是白色
1 从GC Roots开始枚举,所有的直接引用变为灰色(移入灰色集合),GC Roots变为黑色
2 从灰色集合中取出一个灰色对象进行分析 ,将这个对象所有的直接引用变为灰色,放入灰色集合中,将这个对象变为黑色
3 重复步骤2,直到灰色集合为空
4 分析完成,仍然是白色的对象就是GC Roots不可达的对象,可以作为垃圾被清理

2种意外情况
把原本应该垃圾回收的死亡对象错误的标记为存活 , 浮动辣鸡 ,下一次有机会回收
把原本存活的对象错误的标记为已死亡,导致“对象消失”     悬挂指针 


屏障技术
解决并发扫描时的对象消失问题
屏障技术就是在并发或者增量标记过程中保证三色不变性的重要技术

内存屏障 
读屏障(Read barrier)和 写屏障(Write barrier)
读屏障需要在读操作中加入代码片段,对用户程序的性能影响很大,
一般都会采用写屏障保证三色不变性


gc分为4个阶段
golang v1.16
runtime/mgc.go

1. sweep termination(清理终止)​ 
  a. 暂停程序,触发STW。所有的 P(处理器)都会进入 safe-point(安全点)
  b. 清理未被清理的 span 。如果当前垃圾收集是强制触发的,需要处理还未被清理的内存管理单元
  
2. the mark phase(标记阶段)​ 
  a. 将GC状态gcphase从_GCoff改成_GCmark、开启写屏障、启用协助线程(mutator assists)、将根对象入队​ 
  b. 恢复程序执行,标记进程(mark workers)和协助程序会开始并发标记内存中的对象,写屏障会覆盖的重写指针和新指针(标记成灰色),而所有新创建的对象都会被直接标记成黑色;​ 
  c. GC执行根节点的标记,这包括扫描所有的栈、全局对象以及不在堆中的运行时数据结构。扫描goroutine 栈会导致 goroutine 停止,并对栈上找到的所有指针加置灰,然后继续执行 goroutine。​ 
  d. GC遍历灰色对象队列,会将灰色对象变成黑色,并将该指针指向的对象置灰。​ 
  e. 由于GC工作分布在本地缓存中,GC 会使用分布式终止算法(distributed termination algorithm)来检测何时不再有根标记作业或灰色对象,如果没有了 GC 会转为mark termination(标记终止)

3. mark termination(标记终止)​ 
  a. STW​ 
  b. 将GC状态gcphase切换至 _GCmarktermination ,关闭gc工作线程和协助程序​ 
  c. 执行housekeeping,例如刷新mcaches

4. the sweep phase(清理阶段)​ 
  a. 将GC状态gcphase切换至 _GCoff 来准备清理阶段,初始化清理阶段并关闭写屏障​ 
  b. 恢复用户程序,从现在开始,所有新创建的对象会标记成白色;如果有必要,在使用前分配清理spans​ 
  c. 后台并发清理所有的内存管理类单元



java GC 进化 Java GC种类及配置说明

上一篇     下一篇
PostgreSQL encode 函数

MySQL jdbc 版本问题导致 连接错误

java Virtual Threads 虚拟线程

git的几个实用命令

networking io with virtual threads

guice例子