因为 Go 是一种命令式语言,它的值类型,内存访问模式和 C# (.NET 使用分代垃圾收集器)至关。php
事实上,Go 程序一般是处理 request/response 任务(如 HTTP 服务器),这意味着 Go 程序表现出强烈的代际行为,Go 团队正在探索潜在的能够利用代际假说的算法,他们称之为“面向请求的垃圾收集器”。这本质上是一个能够策略调优的分代垃圾收集器。在处理请求/响应这种模式时,经过确保年轻代足够大以使经过处理请求产生的全部垃圾都在其中来优化 GC。(高可用架构译者注:指的是 Go下一代垃圾收集器 Transaction-Oriented Collector)html
尽管如此,Go 的当前 GC 是不分代的。只是在后台运行标记/扫描。(高可用架构译者注:并发标记清除算法)
这样使暂停时间很是短 ,但使其余因素更糟糕。从咱们的基本理论上面咱们能够看到:java
咱们能够看到这些权衡:python
服务1分配内存多于服务2,所以STW暂停在服务1中较高。但STW暂停持续时间在两个服务上都降低了一个数量级。咱们看到切换后,两个服务后在GC中花费的CPU使用率增长了约20%。ios
在这个特定的状况下,Go 以更慢的收集器为代价换取暂停时间的数量级降低。这是一个好的权衡吗?暂停时间已经足够低吗?c++
付出更多的硬件成本以得到较低的暂停时间,在一些状况下未必有意义。若是你的服务器暂停时间从 10msec 下降到 1msec,你的用户真的会注意到吗?若是你必须加倍你的机器数量才能达成这一目的呢?程序员
Go 将暂停时间优化做为首要目标,以致于它彷佛愿意将程序减慢至任何数量级,以得到较短暂停。算法
曾几什么时候,内存管理是程序员开发应用的一大难题。传统的系统级编程语言(主要指C/C++)中,程序员必须对内存当心的进行管理操做,控制内存的申请及释放。稍有不慎,就可能产生内存泄露问题,这种问题不易发现而且难以定位,一直成为困扰开发者的噩梦。如何解决这个头疼的问题呢?编程
过去通常采用两种办法:缓存
为了解决这个问题,后来开发出来的几乎全部新语言(java,python,php等等)都引入了语言层面的自动内存管理 – 也就是语言的使用者只用关注内存的申请而没必要关心内存的释放,内存释放由虚拟机(virtual machine)或运行时(runtime)来自动进行管理。而这种对再也不使用的内存资源进行自动回收的行为就被称为垃圾回收。
这是最简单的一种垃圾回收算法,和以前提到的智能指针殊途同归。对每一个对象维护一个引用计数,当引用该对象的对象被销毁或更新时被引用对象的引用计数自动减一,当被引用对象被建立或被赋值给其余对象时引用计数自动加一。当引用计数为0时则当即回收对象。
这种方法的优势是实现简单,而且内存的回收很及时。这种算法在内存比较紧张和实时性比较高的系统中使用的比较普遍,如ios cocoa框架,php,python等。简单引用计数算法也有明显的缺点:
该方法分为两步,标记从根变量开始迭代得遍历全部被引用的对象,对可以经过应用遍历访问到的对象都进行标记为“被引用”;标记完成后进行清除操做,对没有标记过的内存进行回收(回收同时可能伴有碎片整理操做)。
这种方法解决了引用计数的不足,可是也有比较明显的问题:每次启动垃圾回收都会暂停当前全部的正常代码执行,回收是系统响应能力大大下降!固然后续也出现了不少mark&sweep算法的变种(如三色标记法)优化了这个问题。
通过大量实际观察得知,在面向对象编程语言中,绝大多数对象的生命周期都很是短。分代收集的基本思想是,将堆划分为两个或多个称为 代(generation)的空间。新建立的对象存放在称为 新生代(young generation)中(通常来讲,新生代的大小会比 老年代小不少),随着垃圾回收的重复执行,生命周期较长的对象会被 提高(promotion)到老年代中。所以,新生代垃圾回收和老年代垃圾回收两种不一样的垃圾回收方式应运而生,分别用于对各自空间中的对象执行垃圾回收。新生代垃圾回收的速度很是快,比老年代快几个数量级,即便新生代垃圾回收的频率更高,执行效率也仍然比老年代垃圾回收强,这是由于大多数对象的生命周期都很短,根本无需提高到老年代。
Go的GC自打一开始就被不少人诟病,通过这么多年的发展Go的GC已经变得很是的优秀了,如下是Go的GC算法里程碑
Go语言GC算法主要是基于Mark and Sweep (标记清除)
算法,在此基础上进行改进和优化。
Mark and Sweep
(标记清除)算法主要是如下2个步骤
咱们经过如下图解来解释 标记清除 算法是如何工做的
标记清除
算法存在如下几个问题
STW
指的是runtime把全部的协程都冻结了,意味着用户逻辑是暂停的,这样全部的对象都不会被修改,这个时候去扫描是绝对安全的。
为了解决标记清除
算法带来的问题,Go在标记清除算法基础上提出来Tri-color Marking
(三色标记法)算法来优化GC过程,大致流程以下
白色
的灰色
灰色
,本身标记为黑色
灰色
对象白色
对象黑色
对象变成白色
三色标记法
是怎么优化STW
问题的呢,主要是如下2点
标记操做和用户逻辑并行
: 用户逻辑常常会生成或改变对象引用,那如何保证标记和用户逻辑并行呢?Go为了解决这个问题引入了写屏障
机制,在GC的过程当中会监控对象的内存修改,并对对象进行从新标记,这个时候用户逻辑也能够执行 (其实是很短暂的STW,而后对对象从新标记),因此标记操做能够作到必定程度和用户逻辑并行。清除操做和用户逻辑并行
: 咱们知道三色标记法中最后只剩下的黑白两种对象,黑色对象是程序恢复后接着使用的对象,若是不碰触黑色对象,只清除白色的对象,确定不会影响程序逻辑,因此清除白色对象和用户逻辑能够并行。经过容许用户逻辑在标记和清除操做上作到并行处理来缩短STW的时间,提高总体GC的性能。