GO语言内存管理
1. 协程栈
- 协程栈记录了协程的执行现场,初始空间一般为
2KB。 - 协程栈还负责记录局部变量,传递参数和返回值。
- GO 使用参数拷贝传递。
- GO 协程栈位于 GO 堆内存上,堆内存位于操作系统虚拟内存上。
- 栈空间不够的时候可以申请分段型(早期版本)、连续型新空间。
2. 逃逸分析:
- 栈回收后需要继续使用的变量或者太大的变量,不能放在栈上,应该在堆上,这种现象称为逃逸分析
- 指针逃逸:函数返回了参数的指针。
- 空接口逃逸:如果函数参数为空接口,实参可能逃逸,因为空接口类型的函数往往会使用反射。
- 大变量逃逸:一般超过
64KB的变量就会发生逃逸。
3. Go堆内存结构
- Go 模仿 TCmalloc,建立了自己的堆内存架构。
- 使用 heapArena 向操作系统申请内存。
- 使用 heapArena 时,以 mapan 为单位,防止碎片化。
- mcentral 是 mspan 们的中心索引。
- mcache 记录了分配给各个 P 的本地 mspan。
4. 堆内存分配
- Go对象根据大小分为微对象,小对象和大对象。
- 微小对象直接在本地 mspan 中分配,不够的话再交换新的 mspan。
- 大对象直接从 heapArena 开辟 0 级的 mspan。
3. 三色标记法
-
Go 的垃圾回收采用标记-清除的方式
-
三色:
- 黑色:有用,而且已经扫描完成,不能清除
- 灰色:有用,但是扫描还未完成,不能清除
- 白色:还未扫描或者扫描结束后可以清除的对象
-
过程:
- 起初所有对象都是白色的
- 从根出发扫描可达对象,标记为灰色
- 扫描灰色对象,将其引用的对象标记为灰色,自身标记为黑色
- 分析结束后,清除白色的对象
- 再次标记时,所有对象恢复为白色
-
并行标记:
- 删除屏障:在释放某个对象的指针时候把引用的对象置为灰色,可以杜绝在GC标记中被释放的指针被清除
- 插入屏障:对指针新指向的对象置为灰色,可以杜绝在GC标记中被插入的指针导致误删除
- 混合屏障:Go 采用的方式,被删除和背添加的堆对象标记为灰色
-
触发时机:
- 系统定时触发:2分钟内没有GC时触发
- 用户显式触发:用户调用 runtime.GC 方法
- 申请内存触发:给对象申请空间的时候,可能触发
-
优化:
- 内存池化:缓存对象,防止频繁的创建和删除
- 减少逃逸
- 使用空结构体