本文目录一览:
Golang的垃圾回收
最近垃圾分类的话题热度一下子就上去了,很多人因为垃圾分类的问题很头痛。因为垃圾这个话题,那我就想来说说Golang里面的垃圾,于是就有了这篇博客,golang中的垃圾回收。
现阶段网上针对golang垃圾回收的解析已经很多了,所以我也没有必要仔仔细细的一点点说,还是那个原则,用最直白的话告诉你,垃圾到底是怎么收的。
首先本文后续都会使用 GC 代替垃圾回收这几个字。
我们知道创建对象会给他分配内存资源,如果这个对象不使用了,而这个内存资源却一直被占用的话,那么我们的电脑很快就会被放满,所以需要将这些垃圾对象进行回收。
要回收,那么我们必须知道什么才是垃圾,什么不是垃圾。
在我们看来,一个对象以后都不用了,就是垃圾。
在程序看来,一个对象没有被引用了,就是垃圾。
首先说明一下,下面说的停,都是STW,stop the world,全世界暂停,所有运行的都停下来了。
先告诉所有人,停一下,我来记录一下当前状态。
告诉所有人,你们继续,该干嘛干嘛,我标记一下要用的对象
一开始所有点是白色,首先从根节点出发,标记相连的点为灰色(相连证明有引用),并且将所有灰色的点存起来;
告诉所有人,再停一下,在第二个过程中,因为所有人继续在工作,那么就会产生新的垃圾,因为第一个过程记录了状态,所以需要标记一下新的垃圾;然后清除所有白色的点,因为白色的点是没人引用的,也就是垃圾。
你一定会有这样的疑问:
那么既然会导致那么多问题,为什么不直接停下来,标记完回收完了再开始呢?
因为慢~
所以这样GC的原因是既要保证GC正常执行,又要保证效率,不能停的时间太长。
其实第一次停的时候,启动了一个写屏障 (write barrier)它需要记录后续过程中新创建的对象
这个过程称为三色标记,有点类似广度优先搜索。
这次是必须停,因为在第二个过程中引用会发生变化,从而需要停止后重新扫描一遍;然后关闭写屏障,最后再清理。
开启写屏障时需要stw
关闭写屏障前需要stw
开启写屏障之后的标记过程与其他程序并发执行
关闭写屏障之后的清扫过程与其他程序并发执行
那毕竟GC还是需要STW的,虽然可能停止时间很短,但是对于程序来说,整个程序停止1秒那对于用户来说就是致命打击。所以GC肯定需要一个触发的条件,不能想来就来。
这是一个触发的条件,默认GC百分比设置的是100,意思是,如果这次回收之后总共占用2M的内存,那么下次触发的条件时当超过4M的时候;同理,当这次回收之后总共占用4M,那么下次触发条件就是8M。
这个简单,当一定时间(2分钟)没有执行过GC就触发GC
使用命令 runtime.GC() 手动触发GC
以上就是在golang中垃圾回收的大致流程,总的来说使用三色标记法进行标记清除,并且标记时与程序运行并行,为了解决问题使用写屏障来记录标记过程中对象的变更。总来的来说也是为了提高垃圾回收的效率,并且尽可能的减少STW的时间。
了解下来,与java的分代回收相比,golang中的回收算法理解起来更加简单一些。
【golang】内存逃逸常见情况和避免方式
因为如果变量的内存发生逃逸,它的生命周期就是不可知的,其会被分配到堆上,而堆上分配内存不能像栈一样会自动释放,为了解放程序员双手,专注于业务的实现,go实现了gc垃圾回收机制,但gc会影响程序运行性能,所以要尽量减少程序的gc操作。
1、在方法内把局部变量指针返回,被外部引用,其生命周期大于栈,则溢出。
2、发送指针或带有指针的值到channel,因为编译时候无法知道那个goroutine会在channel接受数据,编译器无法知道什么时候释放。
3、在一个切片上存储指针或带指针的值。比如[]*string,导致切片内容逃逸,其引用值一直在堆上。
4、因为切片的append导致超出容量,切片重新分配地址,切片背后的存储基于运行时的数据进行扩充,就会在堆上分配。
5、在interface类型上调用方法,在Interface调用方法是动态调度的,只有在运行时才知道。
1、go语言的接口类型方法调用是动态,因此不能在编译阶段确定,所有类型结构转换成接口的过程会涉及到内存逃逸发生,在频次访问较高的函数尽量调用接口。
2、不要盲目使用变量指针作为参数,虽然减少了复制,但变量逃逸的开销更大。
3、预先设定好slice长度,避免频繁超出容量,重新分配。
golang打印栈大小
8.1。根据查询golang打印栈官方公布的参数显示,golang打印栈大小为8.1,Go又称Golang,是Google开发的一种静态强类型、编译型、并发型,并具有垃圾回收功能的编程语言。
Golang什么时候会触发GC
Golang采用了三色标记法来进行垃圾回收,那么在什么场景下会触发这个回收动作呢?
源码主要位于文件 src/runtime/mgc.go go version 1.16
触发条件从大方面说,可分为 手动触发 和 系统触发 两种方式。手动触发一般很少用,主要由开发者通过调用 runtime.GC() 函数来实现,而对于系统自动触发是 运行时 根据一些条件判断来进行的,这也正是本文要介绍的内容。
不管哪种触发方式,底层回收机制是一样的,所以我们先看一下手动触发,根据它来找系统触发的条件。
可以看到开始执行GC的是 gcStart() 函数,它有一个 gcTrigger 参数,是一个触发条件结构体,它的结构体也很简单。
其实在Golang 内部所有的GC都是通过 gcStart() 函数,然后指定一个 gcTrigger 的参数来开始的,而手动触发指定的条件值为 gcTriggerCycle 。 gcStart 是一个很复杂的函数,有兴趣的可以看一下源码实现。
对于 kind 的值有三种,分别为 gcTriggerHeap 、 gcTriggerTime 和 gcTriggerCycle 。
运行时会通过 gcTrigger.test() 函数来决定是否需要触发GC,只要满足上面基中一个即可。
到此我们基本明白了这三种触发GC的条件,那么对于系统自动触发这种,Golang 从一个程序的开始到运行,它又是如何一步一步监控到这个条件的呢?
其实 runtime 在程序启动时,会在一个初始化函数 init() 里启用一个 forcegchelper() 函数,这个函数位于 proc.go 文件。
为了减少系统资源占用,在 forcegchelper 函数里会通过 goparkunlock() 函数主动让自己陷入休眠,以后由 sysmon() 监控线程根据条件来恢复这个gc goroutine。
可以看到 sysmon() 会在一个 for 语句里一直判断这个 gcTriggerTime 这个条件是否满足,如果满足的话,会将 forcegc.g 这个 goroutine 添加到全局队列里进行调度(这里 forcegc 是一个全局变量)。
调度器在调度循环 runtime.schedule 中还可以通过垃圾收集控制器的 runtime.gcControllerState.findRunnabledGCWorker 获取并执行用于后台标记的任务。
Golang 真的好用吗?
好用,优点如下:
并发简单、效率高
函数可以返回多个参数
垃圾回收(相比c/c++。不过java、c#都有这个优势)
简单易上手,语言特性少(也算缺点)
配套工具完善(pprof太好用了)
简介
Go(又称Golang)是Google开发的一种静态强类型、编译型、并发型,并具有垃圾回收功能的编程语言。
罗伯特·格瑞史莫(Robert Griesemer),罗勃·派克(Rob Pike)及肯·汤普逊(Ken Thompson)于2007年9月开始设计Go,稍后Ian Lance Taylor、Russ Cox加入项目。Go是基于Inferno操作系统所开发的。Go于2009年11月正式宣布推出,成为开放源代码项目。
并在Linux及Mac OS X平台上进行了实现,后来追加了Windows系统下的实现。在2016年,Go被软件评价公司TIOBE 选为“TIOBE 2016 年最佳语言”。 目前,Go每半年发布一个二级版本(即从a.x升级到a.y)。