内存管理中涉及到内存释放的部分。文中使用Go版本为1.17.6
释放方法
在《Go语言内存管理-底层视角》中提到,位于go/src/runtime/mgcscavenge.go中pageAlloc的方法scavengeRangeLocked会使用sysUnused将一段内存从ready状态转为prepared状态,此时内存可以被操作系统回收,详细看下该方法:
1 | // pageAlloc中根据chunks的索引可以查到一个pallocData,每个pallocData代表一个chunk的bitmap.bitmap中分两部分,一部分是8个uint64,共代表8*64 |
查看调用链,主要如下:
- gcenable->bgscavenge->scavenge->scavengeOne->scavengeRangeLocked
- mheap.grow->scavenge->scavengeOne->scavengeRangeLocked
第一条链路为后台gc进程,第二条链路为通过mheap扩展内存时触发,二者一个是主动一个是被动,首先看mheap扩容时的触发方式
被动触发
1 | if retained := heapRetained(); retained+uint64(totalGrowth) > h.scavengeGoal { |
查看已经申请的内存 + 本次申请的内存是否大于mheap中的scavengeGoal,scavengeGoal会根据GC触发策略动态变化,如果满足条件,则触发一次回收
主动触发
Go程序启动时就会执行gcenable函数,该函数启动一个goroutine执行bgscavenge()函数
bgscavenge()函数会根据权重计算逐步回收pages,但每次只回收一个page。如果没有回收任务可做,则通过调用gopark将自己调度出去。有两种情形可以再将回收goroutine调度执行,如下:
- 通过调用readyForScavenger(当执行完毕sweepone后调用),会将一个标记位置为1,sysmon通过判断该标记位判断是否需要调度回收任务
- 当执行完所有spans的清扫后(finishsweep_m)会将回收任务调度执行
总结
本文主要讲了内存回收流程,最后讲到GC部分会主动回收。具体GC的介绍待后续文章讲解。