golang 关闭gc 并手动gc_走进 Golang 垃圾回收

(给Go开发大全加星标)

来源:DreamService

https://zhuanlan.zhihu.com/p/342792030

【导读】本文结合Golang底层源码和汇编代码,深入解析了go语言垃圾回收机制的设计和实现

0. 冲动&经历&演变

内存管理的目的是什么?目的,防止内存泄漏;核心点,防止程序没有可用内存,『回收』『不再被引用』的堆内存(因为栈内存随时收缩自动释放)。

那么接下来就是,如何知道哪些内存『不再被引用』?

显示管理 常见的有 C 语言,写 C 的人应该都有过内存泄漏的噩梦。后来演生出来 C++,虽然继承了 C 的各种包袱,但尽力通过析构函数、智能指针、异常机制等等途径尝试解决内存问题。

引用计数 c++ 的智能指针便是『引用计数』的思路,如果内存增加了新引用,counter += 1,同理 counter \-= 1,如果 counter == 0 释放内存。包括,python、php 在内的许多语言都采用此等思路。

引用计数每次指针操作要进行 counter 的维护,如果 counter 变成 0 还要去 free, 容易拖慢工作线程。同时,引用计数无法解决循环依赖问题,引入程序 OOM 风险,增大编码工作心智负担。

标记清除 一个与『引用计数』不同的派系,不通过 counter 维护。使用 GC 线程,扫描堆空间的内存依赖关系,标记不被依赖的内存,回收之。如,java/golang 等

附注:策略优缺对比

可见,引用计数是把依赖关系的维护 & 回收内存,分摊到了每次操作。工作线程 顺带做了内存管理的事。相对而言,标记清除是把 GC 工作完全托管给了 GC 线程,工作线程集中精力做业务逻辑。

引用计数没有太多延展空间,java/golang 的 GC 走『标记清除』派系。通过细节优化,以期拿到最大收益:GC 标记线程与工作线程协作:加锁。

如何减少锁粒度?全局大锁(原始的标记清除) => 全局分阶段锁(golang 1.5 的写屏障) => 局部小锁(golang1.7 的混合屏障)=> 期待更徍

1. 标记清除思路

标记出所有不需要回收的对象,在标记完成后统一回收掉所有未被标记的对象。

fc5cc5f36bb62aee4e5a52aa116b24d7.png

细节 以局部变量、全局变量等变量为入口,遍历堆内存的依赖关系。生成『堆节点依赖树』,不在树上的节点为可回收。

4c3f16f16f55abc3e98239a4f236616b.png
// https://github.com/golang/go/blob/release-branch.go1.15/src/runtime/mgcmark.go#L60
// gcMarkRootPrepare queues root scanning jobs (stacks, globals, and some miscellany)
// and initializes scanning-related state.
func gcMarkRootPrepare() {
// Compute how many data and BSS root blocks there are.
nBlocks := func(bytes uintptr) int {
return int(divRoundUp(bytes, rootBlockBytes))
}

work.nDataRoots = 0
work.nBSSRoots = 0
// Scan globals.
for _, datap := range activeModules() {
nDataRoots := nBlocks(datap.edata - datap.data)
if nDataRoots > work.nDataRoots {
work.nDataRoots = nDataRoots
}
}

....
}

2. 全局大锁

遍历图生成依赖树,其准确性依赖于遍历过程图不发生变化。最简单的做法是 Stop The World:通过一把大锁,这个期间除了 GC 线程,其它线程全部暂停。

显而易见,『全局大锁』不算一个极佳的方案。STW 导致程序的服务能力突然中止,而这种中止的时长 & 时机又具有不可遇见性。

所以,优化的重点就是让锁的影响降低……

3. 分阶段加锁

通过细化分析,我们拆成『必 lock』阶段 & 『不必 lock』阶段。比如:

  1. 『无lock』把图遍历一遍,同时记录遍历期间的 modify
  2. STW, 把 modify 的变量再遍历一遍

3.1 如何避免 LOCK

遍历过程中的并发编辑如何处理?

b41dd6042f697cbf2b900a3e6abeaf4e.png

『转移节点』两种处理

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值