GoGc

linux内存布局

要搞懂gc前我们需要知道gc到底在回收什么。而想到知道gc在回收什么不可避免的就必须要清楚进程的内存布局了。


image.png
  1. kernel space 内核空间可以操作任意空间,而用户空间如果需要操纵内核空间,需要由操作系统来完成,调用操作称为系统调用(system call)。
  2. stack是栈区,常称为堆栈。它的分配由高地址往低地址扩展。栈空间用于分配函数的出入参和局部变量
  3. memory mapping是映射区,比如一些外部的动态链接库等。
  4. heap是堆,和数据结构中的堆没有关系,它的分配由低地址往高地址分配。它用于存储应用程序动态申请的对象。
  5. bss是Block Started by Symbol的简称,属于静态内存分配。一般用来存放程序中未初始化的全局变量。
  6. data用来存储一些常量数据,一般用来存储程序中已经初始化的全局变量。
  7. text用于加载程序自身代码段。

go内存管理

image.png

像go这种自带runtime的语言基本上抛弃了传统的内存分配方式,改为自动管理。这样可以自主地实现更好的内存使用模式,比如内存池、预分配等等。这样,不会每次内存分配都需要进行系统调用。go内存没有内存碎片也是因为这种分配模式而避免的。

协程栈虽然在堆上但是也是不需要gc回收

gc负责回收堆内存,而不回收栈(协程栈)中的内存。主要是原因是栈为函数执行准备的,存储着函数的局部变量以及调用栈,这块内存用完会直接释放所以不需要gc来管理。(go的协程栈也是在堆内存分配的,不是传统的栈,go的栈有个stack cache pool,管理栈对象,回收已销毁的栈还给stack cache pool,栈仍旧是调用完毕即销毁,包括栈中的临时对象)。

变量逃逸

逃逸分析基本原则

  1. 指向堆栈对象的指针不能存储在堆中。(堆上的指针不能指向栈)
  2. 指向堆栈对象的指针不能超过那个对象的寿命。
type User struct {
  Name string
    Age int
}
func main() {
    GetUser()
}

func GetUser() *User {
  user := User{}
    return &user
}
/*由于 *user 这个指针被传到 main 函数中使用,而 User 这个对象在 GetUser 方法中 New 出来,如果不逃逸到堆上,则指针的寿命比对象长。因此需要逃逸到堆上,让对象的寿命比其指针长。因此上述例子发生了逃逸
*/

type User struct {
  Name string
    Age int
    Car *Car
}
type Car struct {
}

func main() {
    user := GetUser()
    car := Car{}
    user.Car = &car
}

func GetUser() *User {
    return &User{}
}

/*
`GetUser` 返回的是一个逃逸的对象,即其已经存在堆中。我们给其一个字段赋值 `*Car`, 如果把 Car 对象分配在栈中,发生了指向 栈中对象(Car)的指针存储在堆中对象中(User) ,违背了原则 1,所以 Car 对象需要逃逸到堆。`[fmt.Println](https://link.zhihu.com/?target=https%3A//github.com/golang/go/issues/8618)` 也是这个原因存在逃逸
*/

GC主要流程

根对象

全局变量:程序在编译期就能确定的那些存在于程序整个生命周期的变量。
执行栈:每个 goroutine 都包含自己的执行栈,这些执行栈上包含栈上的变量及指向分配的堆内存区块的指针。
寄存器:寄存器的值可能表示一个指针,参与计算的这些指针可能指向某些赋值器分配的堆内存区块。

三色标记法

三色抽象只是一种描述追踪式回收器的方法,在实践中并没有实际含义,它的重要作用在于从逻辑上严密推导标记清理这种垃圾回收方法的正确性。
当垃圾回收开始时,只有白色对象。随着标记过程开始进行时,灰色对象开始出现(着色),这时候波面便开始扩大。当一个对象的所有子节点均完成扫描时,会被着色为黑色。当整个堆遍历完成时,只剩下黑色和白色对象,这时的黑色对象为可达对象,即存活;而白色对象为不可达对象,即死亡。这个过程可以视为以灰色对象为波面,将黑色对象和白色对象分离,使波面不断向前推进,直到所有可达的灰色对象都变为黑色对象为止的过程。


image.png

GC流程图

这应该是比较老版本的gc流程,但是大致正确。


image.png
  1. Off 代表gc当前未开启,一轮完整的Gc总是从Off状态开启的。
  2. Stack Scan 收集根对象(全局变量和goroutine栈上的变量)这个节点会开启写屏障 。
    a. 不需要回收栈上的对象为什么需要扫描栈上的变量?
    b. 开启写屏障需要进行stw。(这里go 1.14 做了什么优化?)
  3. Mark 标记对象,知道标记完所有根对象和根对象可达对象,此时写屏障会记录所有指针的更改。
  4. Mark Termination 重新扫描部分全局变量和发生更改的栈变量,完成标记。(stw主要耗费时间)
  5. Sweep 并发清除为标记的对象。

GC的优化迭代

上图我们发现第二次的Stw需要很久,go是怎么优化的呢?答案是混合写屏障
首先我们要知道为什么需要stw?
因为标记工作和用户逻辑代码是并行的,标记完成后的栈重新执行用户代码后可能会破坏当前的标记现场。比如一个黑色对象因为业务代码引用了堆上的白色对象,如此垃圾回收的正确性就被破坏了。白色对象后续会被回收,相当于栈上的一个对象引用了一个需要被回收的对象。为此解决这种情况有3种方案:

  1. 栈上使用写屏障,(屏障就是用户对变量操作时插入的特定代码),被扫描后的栈就开启写屏障,所有引入的对象全部被标为黑色。
  2. 在gc过程中扫描后的栈正常运行,不做处理,等扫描阶段结束后,stw,重新对活跃的栈进行扫描。这也是上文做的方式。
  3. 对堆采用混合写屏障,对栈不做处理。栈上运行
image.png

最终go采用了第三种方案,性能和准确性上面都获得了保证。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,980评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,178评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,868评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,498评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,492评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,521评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,910评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,569评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,793评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,559评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,639评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,342评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,931评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,904评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,144评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,833评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,350评论 2 342

推荐阅读更多精彩内容