go:垃圾回收GC触发条件详解


Posted in Golang onApril 24, 2021

版本: go version go1.13 darwin/amd64

在go源码runtime目录中找到gcTrigger结构体,就能看出大致调用的位置

GC调用方式 所在位置 代码
定时调用 runtime/proc.go:forcegchelper() gcStart(gcTrigger{kind: gcTriggerTime, now: nanotime()})
分配内存时调用 runtime/malloc.go:mallocgc() gcTrigger{kind: gcTriggerHeap}
手动调用 runtime/mgc.go:GC() gcStart(gcTrigger{kind: gcTriggerCycle, n: n + 1})

调用入口有了,再进入gcStart

func gcStart(trigger gcTrigger) {
	...省略
	for trigger.test() && sweepone() != ^uintptr(0) {
		sweep.nbgsweep++
	}
	// Perform GC initialization and the sweep termination
	// transition.
	semacquire(&work.startSema)
	// Re-check transition condition under transition lock.
	这里做了双重锁,来判断是否符合GC条件
	if !trigger.test() {
		semrelease(&work.startSema)
		return
	}
	...省略
}
//是否需要触发GC
func (t gcTrigger) test() bool {
	if !memstats.enablegc || panicking != 0 || gcphase != _GCoff {
		return false
	}
	switch t.kind {
	case gcTriggerHeap:
		//gc_trigger是触发标记的堆大小。当heap_live≥gc_trigger时,标记阶段将开始。
		//这也是必须完成比例扫描的堆大小。
		//这是在标记终止期间根据下一个循环的触发器的triggerRatio计算的
		return memstats.heap_live >= memstats.gc_trigger
		
	case gcTriggerTime:
		if gcpercent < 0 {
			return false
		}
		lastgc := int64(atomic.Load64(&memstats.last_gc_nanotime))
		// forcegcperiod = 默认是2分钟
		return lastgc != 0 && t.now-lastgc > forcegcperiod
	case gcTriggerCycle:
		// t.n > work.cycles, but accounting for wraparound.
		return int32(t.n-work.cycles) > 0
	}
	return true
}

后面的代码就是正常的垃圾回收流程了,这里暂且不表,这里只关心gc的触发场景

关于golang垃圾回收,内存分配时何时会重新进入GC?

这里问题是gc的关键,比如当前用了10M内存,随着程序运行,使用内存不是一个固定的值,在当次GC标记结束后,会更新下一次触发gc的heap大小(gc_trigger),下次GC进入之后会在上述的test()函数中会进行heap大小的比较,如果符合条件就真正进行GC

func gcSetTriggerRatio(nextTriggerRatio)

补充:go的垃圾回收机制(GC)

常用的垃圾回收算法

1.引用计数(reference counting):如Python

2.标记-清扫(mark & sweep):如golang

3.复制收集(copy and collection):目前许多商业虚拟机都采用这种垃圾回收算法

Golang 的三色标记法

golang 的垃圾回收(GC)是基于标记清扫算法,这种算法需要进行 STW(stop the world),这个过程就会导致程序是卡顿的,频繁的 GC 会严重影响程序性能. golang 在此基础上进行了改进,通过三色标记清扫法与写屏障来减少 STW 的时间.

三色标记法的流程如下,它将对象通过白、灰、黑进行标记

1.所有对象最开始都是白色.

2.从 root 开始找到所有可达对象,标记为灰色,放入待处理队列。

3.历灰色对象队列,将其引用对象标记为灰色放入待处理队列,自身标记为黑色。

4.循环步骤3直到灰色队列为空为止,此时所有引用对象都被标记为黑色,所有不可达的对象依然为白色,白色的就是需要进行回收的对象。

三色标记法相对于普通标记清扫,减少了 STW 时间. 这主要得益于标记过程是 “on-the-fly” 的,在标记过程中是不需要 STW 的,它与程序是并发执行的,这就大大缩短了 STW 的时间.

写屏障

当标记和程序是并发执行的,这就会造成一个问题. 在标记过程中,有新的引用产生,可能会导致误清扫. 清扫开始前,标记为黑色的对象引用了一个新申请的对象,它肯定是白色的,而黑色对象不会被再次扫描,那么这个白色对象无法被扫描变成灰色、黑色,它就会最终被清扫,而实际它不应该被清扫. 这就需要用到屏障技术,golang 采用了写屏障,作用就是为了避免这类误清扫问题. 写屏障即在内存写操作前,维护一个约束,从而确保清扫开始前,黑色的对象不能引用白色对象.

GC 触发条件

1> 当前内存分配达到一定比例则触发

2> 2 分钟没有触发过 GC 则触发 GC

3> 手动触发,调用 runtime.GC()

以上为个人经验,希望能给大家一个参考,也希望大家多多支持三水点靠木。如有错误或未考虑完全的地方,望不吝赐教。

Golang 相关文章推荐
解决Golang中ResponseWriter的一个坑
Apr 27 Golang
golang DNS服务器的简单实现操作
Apr 30 Golang
golang elasticsearch Client的使用详解
May 05 Golang
完美解决golang go get私有仓库的问题
May 05 Golang
Golang 语言控制并发 Goroutine的方法
Jun 30 Golang
Go语言并发编程 sync.Once
Oct 16 Golang
详解Golang如何优雅的终止一个服务
Mar 21 Golang
Golang使用Panic与Recover进行错误捕获
Mar 22 Golang
golang三种设计模式之简单工厂、方法工厂和抽象工厂
Apr 10 Golang
Golang 切片(Slice)实现增删改查
Apr 22 Golang
Golang 实现WebSockets
Apr 24 Golang
Go语言测试库testify使用学习
Jul 23 Golang
基于go interface{}==nil 的几种坑及原理分析
Apr 24 #Golang
golang interface判断为空nil的实现代码
Apr 24 #Golang
golang判断key是否在map中的代码
Apr 24 #Golang
Go语言操作数据库及其常规操作的示例代码
Apr 21 #Golang
为什么不建议在go项目中使用init()
Apr 12 #Golang
Golang二维切片初始化的实现
Apr 08 #Golang
go语言map与string的相互转换的实现
Apr 07 #Golang
You might like
php程序之die调试法 快速解决错误
2009/09/17 PHP
php自定义函数call_user_func和call_user_func_array详解
2011/07/14 PHP
两款万能的php分页类
2015/11/12 PHP
详解thinkphp实现excel数据的导入导出(附完整案例)
2016/12/29 PHP
php脚本守护进程原理与实现方法详解
2017/07/20 PHP
PHP封装的非对称加密RSA算法示例
2018/05/28 PHP
Jquery插件之多图片异步上传
2010/10/20 Javascript
ASP.NET jQuery 实例2 (表单中使用回车在TextBox之间向下移动)
2012/01/13 Javascript
javascript实现了照片拖拽点击置顶的照片墙代码
2015/04/03 Javascript
浅谈被jQuery抛弃的函数及替代函数
2015/05/03 Javascript
jQuery实现的动态伸缩导航菜单实例
2015/05/07 Javascript
javascript图片滑动效果实现
2021/01/28 Javascript
js立即执行函数: (function ( ){})( ) 与 (function ( ){}( )) 有什么区别?
2015/11/18 Javascript
jQuery+css3实现转动的正方形效果(附demo源码下载)
2016/01/27 Javascript
jQuery+ajax简单实现文件上传的方法
2016/06/03 Javascript
jQuery通过ajax方法获取json数据不执行success的原因及解决方法
2016/10/15 Javascript
解析如何利用iframe标签以及js制作时钟
2016/12/08 Javascript
Angularjs通过指令监听ng-repeat渲染完成后执行脚本的方法
2016/12/31 Javascript
关于node-bindings无法在Electron中使用的解决办法
2018/12/18 Javascript
详解key在Vue列表渲染时究竟起到了什么作用
2019/04/20 Javascript
vue两组件间值传递 $router.push实现方法
2019/05/15 Javascript
微信小程序文章列表功能完整实例
2020/06/03 Javascript
[01:03:00]DOTA2上海特级锦标赛A组败者赛 EHOME VS CDEC第一局
2016/02/25 DOTA
python实现dnspod自动更新dns解析的方法
2014/02/14 Python
Python实现屏幕截图的代码及函数详解
2016/10/01 Python
python cv2截取不规则区域图片实例
2019/12/21 Python
详解基于canvas的视频遮罩插件
2018/01/04 HTML / CSS
加拿大租车网站:Enterprise Rent-A-Car
2018/07/26 全球购物
美国农场商店:Blain’s Farm & Fleet
2020/01/17 全球购物
性能测试工程师的面试题
2015/02/20 面试题
企业2014年度工作总结
2014/12/10 职场文书
2014年车间主任工作总结
2014/12/10 职场文书
MySQL 用 limit 为什么会影响性能
2021/09/15 MySQL
Dashboard管理Kubernetes集群与API访问配置
2022/04/01 Servers
python神经网络 tf.name_scope 和 tf.variable_scope 的区别
2022/05/04 Python
Vite + React从零开始搭建一个开源组件库
2022/06/25 Javascript