Golang垃圾回收
2020-12-08 14:46:27 7 举报
AI智能生成
Golang垃圾回收
作者其他创作
大纲/内容
实现原理
流程图
回收阶段
清理终止阶段
暂停程序,所有的处理器在这时会进入安全点(Safe point)
如果当前垃圾收集循环是强制触发的,我们还需要处理还未被清理的内存管理单元
标记阶段
将状态切换至 _GCmark、开启写屏障、用户程序协助(Mutator Assiste)并将根对象入队
恢复执行程序,标记进程和用于协助的用户程序会开始并发标记内存中的对象,写屏障会将被覆盖的指针和新指针都标记成灰色,而所有新创建的对象都会被直接标记成黑色
开始扫描根对象,包括所有 Goroutine 的栈、全局对象以及不在堆中的运行时数据结构,扫描 Goroutine 栈期间会暂停当前处理器
依次处理灰色队列中的对象,将对象标记成黑色并将它们指向的对象标记成灰色
使用分布式的终止算法检查剩余的工作,发现标记阶段完成后进入标记终止阶段
标记终止阶段
暂停程序、将状态切换至 _GCmarktermination 并关闭辅助标记的用户程序
清理处理器上的线程缓存
清理阶段
将状态切换至 _GCoff 开始清理阶段,初始化清理状态并关闭写屏障
恢复用户程序,所有新创建的对象会标记成白色
后台并发清理所有的内存管理单元,当 Goroutine 申请新的内存管理单元时就会触发清理
回收的触发
触发条件
条件详解
runtime.sysmon 和 runtime.forcegchelper — 后台运行定时检查和垃圾收集
runtime.GC — 用户程序手动触发垃圾收集;
runtime.mallocgc — 申请内存时根据堆大小触发垃圾收集;
启动阶段
两次调用 runtime.gcTrigger.test 方法检查是否满足垃圾收集条件
暂停程序、在后台启动用于处理标记任务的工作 Goroutine、确定所有内存管理单元都被清理以及其他标记阶段开始前的准备工作
进入标记阶段、准备后台的标记工作、根对象的标记工作以及微对象、恢复用户程序,进入并发扫描和标记阶段
在垃圾收集启动期间,运行时会调用 runtime.gcBgMarkStartWorkers 为全局每个处理器创建用于执行后台标记任务的 Goroutine,每一个 Goroutine 都会运行 runtime.gcBgMarkWorker,所有运行 runtime.gcBgMarkWorker 的 Goroutine 在启动后都会陷入休眠等待调度器的唤醒
暂停与恢复
程序恢复过程会使用 runtime.stopTheWorldWithSema
暂停程序主要使用了 runtime.preemptall 函数,该函数会调用我们在前面介绍过的 runtime.preemptone,因为程序中活跃的最大处理数为 gomaxprocs,所以 runtime.stopTheWorldWithSema 在每次发现停止的处理器时都会对该变量减一,直到所有的处理器都停止运行。该函数会依次停止当前处理器、等待处于系统调用的处理器以及获取并抢占空闲的处理器,处理器的状态在该函数返回时都会被更新至 _Pgcstop,等待垃圾收集器的重新唤醒
程序恢复过程会使用 runtime.startTheWorldWithSema
调用 runtime.netpoll 从网络轮询器中获取待处理的任务并加入全局队列
调用 runtime.procresize 扩容或者缩容全局的处理器
调用 runtime.notewakeup 或者 runtime.newm 依次唤醒处理器或者为处理器创建新的线程
如果当前待处理的 Goroutine 数量过多,创建额外的处理器辅助完成任务
并发扫描与标记辅助
标记辅助
分配多少内存就需要完成多少标记任务
辅助标记的动态平衡
每个 Goroutine 持有的 gcAssistBytes 表示当前协程辅助标记的字节数,全局垃圾收集控制器持有的 bgScanCredit 表示后台协程辅助标记的字节数,当本地 Goroutine 分配了较多的对象时,可以使用公用的信用 bgScanCredit 偿还
并发标记
runtime.gcBgMarkWorker 是后台的标记任务执行的函数,该函数的循环中执行了对内存中对象图的扫描和标记
获取当前处理器以及 Goroutine 打包成 parkInfo 类型的结构体并主动陷入休眠等待唤醒
根据处理器上的 gcMarkWorkerMode 模式决定扫描任务的策略
所有标记任务都完成后,调用 runtime.gcMarkDone 方法完成标记阶段
工作池
工作流程
写屏障、根对象扫描和栈扫描都会向工作池中增加额外的灰色对象等待处理,而对象的扫描过程会将灰色对象标记成黑色,同时也可能发现新的灰色对象,当工作队列中不包含灰色对象时,整个扫描过程就会结束
标记终止
当所有处理器的本地任务都完成并且不存在剩余的工作 Goroutine 时,后台并发任务或者辅助标记的用户程序会调用 runtime.gcMarkDone 通知垃圾收集器。当所有可达对象都被标记后,该函数会将垃圾收集的状态切换至 _GCmarktermination
内存清理
对象回收器在内存管理单元中查找并释放未被标记的对象,但是如果 runtime.mspan 中的所有对象都没有被标记,整个单元就会被直接回收,该过程会被 runtime.mcentral.cacheSpan 或者 runtime.sweepone 异步触发
内存单元回收器会在内存中查找所有的对象都未被标记的 runtime.mspan,该过程会被 runtime.mheap.reclaim 触发
0 条评论
下一页