谈论Go 什么时候会触发 GC问题
怎么入门Golang编程?需要学习哪些知识点?这是新手们刚接触编程时常见的问题;下面golang学习网就来给大家整理分享一些知识点,希望能够给初学者一些帮助。本篇文章就来介绍《谈论Go 什么时候会触发 GC问题》,涉及到触发、GC,有需要的可以收藏一下
在早期经常遭到唾弃的就是在垃圾回收(下称:GC)机制中 STW(Stop-The-World)的时间过长。那么这个时候,我们又会好奇一点,作为 STW 的起始,Go 语言中什么时候才会触发 GC 呢?
1、什么是 GC
在计算机科学中,垃圾回收(GC)是一种自动管理内存的机制,垃圾回收器会去尝试回收程序不再使用的对象及其占用的内存。
最早 John McCarthy
在 1959 年左右发明了垃圾回收,以简化 Lisp
中的手动内存管理的机制(来自 @wikipedia)。
2、为什么要 GC
手动管理内存挺麻烦,管错或者管漏内存也很糟糕,将会直接导致程序不稳定(持续泄露)甚至直接崩溃。
3、GC 触发场景
GC 触发的场景主要分为两大类,分别是:
- 系统触发:运行时自行根据内置的条件,检查、发现到,则进行 GC 处理,维护整个应用程序的可用性。
- 手动触发:开发者在业务代码中自行调用
runtime.GC
方法来触发 GC 行为。
3.1系统触发
在系统触发的场景中,Go 源码的 src/runtime/mgc.go
文件,明确标识了 GC 系统触发的三种场景,分别如下:
const ( gcTriggerHeap gcTriggerKind = iota gcTriggerTime gcTriggerCycle )
gcTriggerHeap:
当所分配的堆大小达到阈值(由控制器计算的触发堆的大小)时,将会触发。gcTriggerTime:
当距离上一个 GC 周期的时间超过一定时间时,将会触发。-时间周期以runtime.forcegcperiod
变量为准,默认 2 分钟。gcTriggerCycle:
如果没有开启 GC,则启动 GC。
在手动触发的 runtime.GC
方法中涉及。
3.2手动触发
在手动触发的场景下,Go 语言中仅有 runtime.GC
方法可以触发,也就没什么额外的分类的。
但我们要思考的是,一般我们在什么业务场景中,要涉及到手动干涉 GC,强制触发他呢?
需要手动强制触发的场景极其少见,可能会是在某些业务方法执行完后,因其占用了过多的内存,需要人为释放。又或是 debug
程序所需。
3.3 基本流程
在了解到 Go 语言会触发 GC 的场景后,我们进一步看看触发 GC 的流程代码是怎么样的,我们可以借助手动触发的 runtime.GC
方法来作为突破口。
核心代码如下:
func GC() { n := atomic.Load(&work.cycles) gcWaitOnMark(n) gcStart(gcTrigger{kind: gcTriggerCycle, n: n + 1}) gcWaitOnMark(n + 1) for atomic.Load(&work.cycles) == n+1 && sweepone() != ^uintptr(0) { sweep.nbgsweep++ Gosched() } for atomic.Load(&work.cycles) == n+1 && atomic.Load(&mheap_.sweepers) != 0 { Gosched() } mp := acquirem() cycle := atomic.Load(&work.cycles) if cycle == n+1 || (gcphase == _GCmark && cycle == n+2) { mProf_PostSweep() } releasem(mp) }
在开始新的一轮 GC 周期前,需要调用 gcWaitOnMark
方法上一轮 GC 的标记结束(含扫描终止、标记、或标记终止等)。
开始新的一轮 GC 周期,调用 gcStart
方法触发 GC 行为,开始扫描标记阶段。
需要调用 gcWaitOnMark
方法等待,直到当前 GC 周期的扫描、标记、标记终止完成。
需要调用 sweepone
方法,扫描未扫除的堆跨度,并持续扫除,保证清理完成。在等待扫除完毕前的阻塞时间,会调用 Gosched
让出。
在本轮 GC 已经基本完成后,会调用 mProf_PostSweep 方法。以此记录最后一次标记终止时的堆配置文件快照。
结束,释放 M。
3.4 在哪触发
看完 GC 的基本流程后,我们有了一个基本的了解。但可能又有小伙伴有疑惑了?
本文的标题是 “GC 什么时候会触发 GC”,虽然我们前面知道了触发的时机。但是....Go 是哪里实现的触发的机制,似乎在流程中完全没有看到?
4、监控线程
实质上在 Go 运行时(runtime)
初始化时,会启动一个 goroutine
,用于处理 GC 机制的相关事项。
代码如下:
func init() { go forcegchelper() } func forcegchelper() { forcegc.g = getg() lockInit(&forcegc.lock, lockRankForcegc) for { lock(&forcegc.lock) if forcegc.idle != 0 { throw("forcegc: phase error") } atomic.Store(&forcegc.idle, 1) goparkunlock(&forcegc.lock, waitReasonForceGCIdle, traceEvGoBlock, 1) // this goroutine is explicitly resumed by sysmon if debug.gctrace > 0 { println("GC forced") } gcStart(gcTrigger{kind: gcTriggerTime, now: nanotime()}) } }
在这段程序中,需要特别关注的是在 forcegchelper
方法中,会调用 goparkunlock
方法让该 goroutine
陷入休眠等待状态,以减少不必要的资源开销。
在休眠后,会由 sysmon
这一个系统监控线程来进行监控、唤醒等行为:
func sysmon() { ... for { ... // check if we need to force a GC if t := (gcTrigger{kind: gcTriggerTime, now: now}); t.test() && atomic.Load(&forcegc.idle) != 0 { lock(&forcegc.lock) forcegc.idle = 0 var list gList list.push(forcegc.g) injectglist(&list) unlock(&forcegc.lock) } if debug.schedtrace > 0 && lasttrace+int64(debug.schedtrace)*1000000 0) } unlock(&sched.sysmonlock) } }
这段代码核心的行为就是不断地在 for 循环中,对 gcTriggerTime
和 now
变量进行比较,判断是否达到一定的时间(默认为 2 分钟)。
若达到意味着满足条件,会将 forcegc.g
放到全局队列中接受新的一轮调度,再进行对上面 forcegchelper
的唤醒。
5、堆内存申请
在了解定时触发的机制后,另外一个场景就是分配的堆空间的时候,那么我们要看的地方就非常明确了。
那就是运行时申请堆内存的 mallocgc
方法。核心代码如下:
func mallocgc(size uintptr, typ *_type, needzero bool) unsafe.Pointer { shouldhelpgc := false ... if size小对象:如果申请小对象时,发现当前内存空间不存在空闲跨度时,将会需要调用
nextFree
方法获取新的可用的对象,可能会触发 GC 行为。大对象:如果申请大于 32k 以上的大对象时,可能会触发 GC 行为。
总结
在这篇文章中,我们介绍了 Go 语言触发 GC 的两大类场景,并分别基于大类中的细分场景进行了一一说明。今天关于《谈论Go 什么时候会触发 GC问题》的内容介绍就到此结束,如果有什么疑问或者建议,可以在golang学习网公众号下多多回复交流;文中若有不正之处,也希望回复留言以告知!

- 上一篇
- Go语言变量与基础数据类型详情

- 下一篇
- 详解如何在Go服务中做链路追踪
-
- 落后的向日葵
- 这篇文章内容太及时了,作者加油!
- 2023-02-04 08:26:30
-
- 激动的老师
- 很有用,一直没懂这个问题,但其实工作中常常有遇到...不过今天到这,看完之后很有帮助,总算是懂了,感谢老哥分享技术贴!
- 2023-01-06 08:56:05
-
- 土豪的小鸽子
- 太全面了,mark,感谢大佬的这篇技术贴,我会继续支持!
- 2023-01-05 00:32:56
-
- 现代的招牌
- 这篇文章内容真及时,细节满满,很有用,已收藏,关注大佬了!希望大佬能多写Golang相关的文章。
- 2023-01-02 19:37:50
-
- Golang · Go教程 | 12分钟前 |
- DebianFTPServer如何限制并发连接数
- 470浏览 收藏
-
- Golang · Go教程 | 31分钟前 |
- Rust在Debian上的环境配置指南
- 393浏览 收藏
-
- Golang · Go教程 | 17小时前 |
- Laravel在Debian上的数据备份攻略
- 273浏览 收藏
-
- Golang · Go教程 | 19小时前 |
- Debian上Kafka主题创建详细指南
- 252浏览 收藏
-
- Golang · Go教程 | 21小时前 |
- Debian系统Dumpcap抓包详细教程
- 235浏览 收藏
-
- Golang · Go教程 | 22小时前 |
- Debian上Tigervnc分辨率设置攻略
- 408浏览 收藏
-
- Golang · Go教程 | 23小时前 |
- Go新版垃圾回收机制优化及FAQ
- 162浏览 收藏
-
- Golang · Go教程 | 23小时前 | 性能优化 strings.Builder bytes.Buffer strconv.ParseInt utf8
- Go语言字符串操作优化与常见问题解读
- 492浏览 收藏
-
- Golang · Go教程 | 1天前 |
- DebianCPUInfo出错的终极解决方案
- 444浏览 收藏
-
- Golang · Go教程 | 1天前 |
- Go语言切片与数组易混淆问题详解
- 453浏览 收藏
-
- Golang · Go教程 | 1天前 |
- Debian上Flutter更新终极指南
- 408浏览 收藏
-
- Golang · Go教程 | 1天前 |
- Go语言time包定时任务易错点深度解析
- 324浏览 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 542次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 508次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 497次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 484次学习
-
- PPTFake答辩PPT生成器
- PPTFake答辩PPT生成器,专为答辩准备设计,极致高效生成PPT与自述稿。智能解析内容,提供多样模板,数据可视化,贴心配套服务,灵活自主编辑,降低制作门槛,适用于各类答辩场景。
- 22次使用
-
- Lovart
- SEO摘要探索Lovart AI,这款专注于设计领域的AI智能体,通过多模态模型集成和智能任务拆解,实现全链路设计自动化。无论是品牌全案设计、广告与视频制作,还是文创内容创作,Lovart AI都能满足您的需求,提升设计效率,降低成本。
- 21次使用
-
- 美图AI抠图
- 美图AI抠图,依托CVPR 2024竞赛亚军技术,提供顶尖的图像处理解决方案。适用于证件照、商品、毛发等多场景,支持批量处理,3秒出图,零PS基础也能轻松操作,满足个人与商业需求。
- 34次使用
-
- PetGPT
- SEO摘要PetGPT 是一款基于 Python 和 PyQt 开发的智能桌面宠物程序,集成了 OpenAI 的 GPT 模型,提供上下文感知对话和主动聊天功能。用户可高度自定义宠物的外观和行为,支持插件热更新和二次开发。适用于需要陪伴和效率辅助的办公族、学生及 AI 技术爱好者。
- 34次使用
-
- 可图AI图片生成
- 探索快手旗下可灵AI2.0发布的可图AI2.0图像生成大模型,体验从文本生成图像、图像编辑到风格转绘的全链路创作。了解其技术突破、功能创新及在广告、影视、非遗等领域的应用,领先于Midjourney、DALL-E等竞品。
- 56次使用
-
- 图解Golang的GC垃圾回收算法
- 2022-12-29 295浏览