Skip to content

Latest commit

 

History

History
50 lines (38 loc) · 4.99 KB

runtime.md

File metadata and controls

50 lines (38 loc) · 4.99 KB

golang 的 runtime 在 golang 中的地位类似于 Java 的虚拟机。不过 go runtime 不是虚拟机.编译时,golang 会将 runtime 部分代码链接进去. alex:可疑理解为java是一台机器一个runtime,go 是一个program 一个runtime???

golang 的 runtime 核心功能包括以下内容:

  • 协程(goroutine)调度(并发调度模型)
  • 垃圾回收(GC)
  • 内存分配
  • 使得 golang 可以支持如 pprof、trace、race 的检测
  • 支持 golang 的内置类型 channel、map、slice、string等的实现
  • 等等

gc

runtime,支持垃圾回收,这属于动态语言的特性之一吧。现在看到不少文章说go的gc消耗时间比较长。所以programmer还是不要滥用,及时释放内存为佳。 常用的垃圾回收算法

  • 引用计数(reference counting): Python 便主要采用的是引用计数的方式,每一个对象都会记录它的引用数,每当有新的引用则值增加,删除则减少,直到引用值为 0 ,则该对象的生命周期结束.
  • 标记-清扫(mark & sweep): 使用标记清扫算法,未引用的对象并不会立刻被清除,而是被标记. 直到内存耗尽,挂起程序,清扫所有未被引用的对象,然后继续程序. 标记清扫法跟踪了 root 访问的所有对象,它可以有效的处理循环引用. 它有一个问题是需要 STW (stop the world). golang 便是采用的标记-清扫法进行垃圾回收.在 golang 的迭代过程中改进为三色标记清扫法,用来减少 STW 的影响.
  • 复制收集(copy and collection): 目前许多商业虚拟机都采用这种垃圾回收算法. 它将内存分为两部分,只使用其中一部分,在进行垃圾回收时,将存活的对象复制到另一部分. 然后清理所有第一部分内存使其构成完成一块,从而避免内存碎片.

golang 的垃圾回收是基于标记清扫算法,这种算法需要进行 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()

内存分配

Tcmalloc(Thread Caching Malloc) 是 google 为 c 语言开发的运行时内存分配算法. 其核心思想是多级管理,从而降低锁的粒度. Go runtime 的内存分配就采用了 Tcmalloc 算法.

Go 程序在启动时,会首先向系统申请一块内存(虚拟地址空间),然后自己切成小块进行管理. 将申请的内存,分成 3 个区域,spans、bitmap、arena,如下图4,这三个区域的作用如下.

  • arena: 就是堆区,go runtime 在动态分配的内存都在这个区域,并且将内存块分成 8kb 的页,一些组合起来的称为 mspan,成为 go 中内存管理的基本单元,这种连续的页一般是操作系统的内存页几倍大小.
  • bitmap: 顾名思义,用来标记堆区使用的映射表,它记录了哪些区域保存了对象,对象是否包含指针,以及 GC 的标记信息.
  • spans: 存放 mspan 的指针,根据 spans 区域的信息可以很容易找到 mspan. 它可以在 GC 时更快速的找到的大块的内存 mspan.

goroutine 相关函数

  • Goexit 退出当前执行的goroutine,但是defer函数还会继续调用
  • Gosched 让出当前goroutine的执行权限,调度器安排其他等待的任务运行,并在下次某个时候从该位置恢复执行。
  • NumCPU 返回 CPU 核数量
  • NumGoroutine 返回正在执行和排队的任务总数
  • GOMAXPROCS 用来设置可以并行计算的CPU核数的最大值,并返回之前的值。