Below you will find pages that utilize the taxonomy term “runtime”
March 29, 2021
Runtime: Golang 定时器实现原理及源码解析
"定时器作为开发经常使用的一种数据类型,是每个开发者需要掌握的,对于一个高级开发很有必要了解它的实现原理,今天我们runtime源码来学习一下它的底层实现。\n定时器分两种,分别为 Timer 和 Ticker,两者差不多,这里重点以Timer为例。\n源文件位于 [src/time/sleep.go](https://github.com/golang/go/blob/go1.16.2/src/time/sleep.go) 和 [src/time/tick.go](https://github.com/golang/go/blob/go1.16.2/src/time/tick.go) 。 go version 1.16.2\n数据结构 Timer 数据结构\n// src/runtime/sleep.go // The Timer type represents a single event. // When the Timer expires, the current time will be sent on C, // unless the Timer was created by …"
March 20, 2021
Runtime: Golang 之 sync.Pool 源码分析
"Pool 指一组可以单独保存和恢复的 临时对象。Pool 中的对象随时都有可能在没有收到任何通知的情况下被GC自动销毁移除。\n多个goroutine同时操作Pool是并发安全的。\n源文件为 [src/sync/pool.go](https://github.com/golang/go/blob/master/src/sync/pool.go) go version: 1.16.2\n为什么使用Pool 在开发高性能应用时,经常会有一些完全相同的对象需要频繁的创建和销毁,每次创建都需要在堆中分配对象,等使用完毕后,这些对象需要等待GC回收。我们知道在Golang中使用三色标记法进行垃圾回收的,在回收期间会有一个短暂STW(stop the world)的时间段,这样就会导致程序性能下降。\n那么能否实现类似数据库连接池这种效果,用来避免对象的频繁创建和销毁,达到尽可能的资源复用呢?为了实现这种需求,标准库中有了sync.Pool 这个数据结构。看名字很知道它是一个池。但是它和我们想象中的数据库连接池还是有些差别的。对于数据库连接池这种资源只要不手动释放就可以一直利用, …"
March 1, 2021
Golang 的调度策略之G的窃取
"我们上篇文章( Golang 的底层引导流程/启动顺序)介绍了一个golang程序的启动流程,在文章的最后对于最重要的一点“调度“ (函数 [schedule()](https://github.com/golang/go/blob/go1.15.6/src/runtime/proc.go#L2607-L2723)) 并没有展开来讲,今天我们继续从源码来分析一下它的调度机制。\n在此之前我们要明白golang中的调度主要指的是什么?在 src/runtime/proc.go 文件里有一段注释这样写到\n// Goroutine scheduler\n// The scheduler’s job is to distribute ready-to-run goroutines over worker threads.\n这里指如何找一个已准备好运行的 G 关联到PM 让其执行。对于G 的调度可以围绕三个方面来理解:\n时机:什么时候关联(调度)。对于调度时机一般是指有空闲P的时候都会去找G执行 对象:选择哪个G进行调度。这是我们本篇要讲的内容 机制:如何调度。execute() 函数 理解了这三个 …"
February 27, 2021
Runtime: Golang是如何处理系统调用阻塞的?
"我们知道在Golang中,当一个Goroutine由于执行 系统调用 而阻塞时,会将M从GPM中分离出去,然后P再找一个G和M重新执行,避免浪费CPU资源,那么在内部又是如何实现的呢?今天我们还是通过学习Runtime源码的形式来看下他的内部实现细节有哪些?\ngo version 1.15.6\n我们知道一个P有四种运行状态,而当执行系统调用函数阻塞时,会从 _Prunning 状态切换到 _Psyscall,等系统调用函数执行完毕后再切换回来。P的状态切换\n从上图我们可以看出 P 执行系统调用时会执行 [entersyscall()](https://github.com/golang/go/blob/go1.15.6/src/runtime/proc.go#L3134-L3142) 函数(另还有一个类似的阻塞函数 entersyscallblock() ,注意两者的区别)。当系统调用执行完毕切换回去会执行 exitsyscall() 函数,下面我们看一下这两个函数的实现。\n进入系统调用 // Standard syscall entry used by the go syscall …"
February 17, 2021
Runtime: 创建一个goroutine都经历了什么?
"我们都知道goroutine的在golang中发挥了很大的作用,那么当我们创建一个新的goroutine时,它是怎么一步一步创建的呢?都经历了哪些操作呢?今天我们通过源码来剖析一下创建goroutine都经历了些什么?go version 1.15.6\n对goroutine最关键的两个函数是 [newproc()](https://github.com/golang/go/blob/go1.15.6/src/runtime/proc.go#L3535-L3564) 和 [newproc1()](https://github.com/golang/go/blob/go1.15.6/src/runtime/proc.go#L3566-L3674),而 newproc1() 函数是我们最需要关注的。\n函数 newproc() 我们先看一个简单的创建goroutine的例子,找出来创建它的函数。\npackage main func start(a, b, c int64) { _ = a + b + c } func main() { go start(7, 2, 5) } 输出结果:\n➜ …"
February 15, 2021
Runtime: 理解Golang中接口interface的底层实现
"接口类型是Golang中是一种非常非常常见的数据类型,每个开发人员都很有必要知道它到底是如何使用的,如果了解了它的底层实现就对开发就更有帮助了。\n接口的定义 在Golang中 interface 通常是指实现了一 组抽象方法的集合,它提供了一种无侵入式的方式。当你实现了一个接口中指定的所有方法的时候,那么就实现了这个接口,在Golang中对它的实现并不需要 implements 关键字。\n有时候我们称这种模型叫做鸭子模型(Duck typing),维基百科对鸭子模型的定义是\n”If it looks like a duck, swims like a duck, and quacks like a duck, then it probably is a duck.“\n翻译过来就是 ”如果它看起来像鸭子,像鸭子一样游泳,像鸭子一样嘎嘎叫,那他就可以认为是鸭子“。\nGo 不同版本之间interface的结构可能不太一样,但整体都差不多,这里使用的Go版本为 1.15.6。\n数据结构 Go 中 interface 在运行时可分 eface 和 iface 两种数据结构,我们先看一下对它们的定 …"
February 13, 2021
认识Golang中的sysmon监控线程
"Go Runtime 在启动程序的时候,会创建一个独立的 M 作为监控线程,称为 sysmon,它是一个系统级的 daemon 线程。这个sysmon 独立于 GPM 之外,也就是说不需要P就可以运行,因此官方工具 go tool trace 是无法追踪分析到此线程( 源码)。sysmon\n在程序执行期间 sysmon 每隔 20us~10ms 轮询执行一次( 源码),监控那些长时间运行的 G 任务, 然后设置其可以被强占的标识符,这样别的 Goroutine 就可以抢先进来执行。\n// src/runtime/proc.go // forcegcperiod is the maximum time in nanoseconds between garbage // collections. If we go this long without a garbage collection, one // is forced to run. // // This is a variable for testing purposes. It normally doesn\u0026#39;t …"
January 22, 2021
Golang 的底层引导流程/启动顺序
"在Golang中,程序的执行入口为 main() 函数,那么底层又是如何工作的呢? 这个问题的答案我们可以在runtime源码找到。对它的解释主要在 [src/runtime/proc.go](https://github.com/golang/go/blob/go1.15.6/src/runtime/proc.go) 文件,下面我们看一下它是如何一步一步开始执行的。go version 1.15.6\n在文件头部有一段对 [Goroutine scheduler](https://github.com/golang/go/blob/go1.15.6/src/runtime/proc.go#L19) 的介绍,我们先了解一下。\n调度器的工作是分发goroutines到工作线程让其运行。一句话指明了调度器的存在意义,就是指挥协调GPM干活。\n主要包含三部分 G 指的是 goroutine M 工作线程,也叫machine P 处理器(逻辑CPU),执行 Go code 的一种资源。这里的Go code 其实就是 goroutine里的代码。\nM必须被指派给P去执行 Go code, 但可以被 …"
January 18, 2021
Runtime: Golang中channel实现原理源码分析
"channel是golang中特有的一种数据结构,通常与goroutine一起使用,下面我们就介绍一下这种数据结构。\nchannel数据结构 channel 是Golang 中最重要的一个数据结构,源码里对应的结构体是hchan,当我们创建一个channel 的时候,实际上是创建了一个hchan结构体。\nhchan结构体 // src/runtime/chan.go type hchan struct { qcount uint // total data in the queue dataqsiz uint // size of the circular queue buf unsafe.Pointer // points to an array of dataqsiz elements elemsize uint16 closed uint32 elemtype *_type // element type sendx uint // send index recvx uint // receive index recvq waitq // list of recv waiters …"
January 11, 2021
Runtime:源码解析Golang 的map实现原理
"go version 1.15.6\nmap作为一种常见的 key-value 数据结构,不同语言的实现原理基本差不多。首先在系统里分配一段连接的内存地址作为数组,然后通过对map键进行hash算法(最终将键转换成了一个整型数字)定位到不同的桶bucket(数组的索引位置),然后将值存储到对应的bucket里\n理想的情况下是一个bucket存储一个值,即数组的形式,时间复杂度为O(1)。\n如果存在键值碰撞的话,可以通过 链表法 或者 开放寻址法 来解决。\n链表法\n开放寻址法\n对于开放寻址法有多种算法,常见的有线性探测法,线性补偿探测法,随机探测法等,这里不再介绍。\nmap基本数据结构 hmap结构体 map的核心数据结构定义在 /runtime/map.go\n// A header for a Go map. type hmap struct { // Note: the format of the hmap is also encoded in cmd/compile/internal/gc/reflect.go. // Make sure this stays in sync …"