Go的具体实现

数据结构

String

<data []Byte, len int>

UTF-8编码

Slice

<data []T, len int, cap int>

扩容规则

  1. 预估扩容后容量

  2. 分配内存,匹配合适的内存规格

Map

Go语言的Map是HashMap

哈希表

(使用渐进式rehash进行扩容)

上图中右上角的结构即为bmap,上方是topHash值,接下来的分别是[]keys, []values

扩容规则

闭包

Go 语言中函数是头等对象,可以作为参数传递,实际上传递的是指针FunctionValue

下图是一个闭包和变量逃逸的例子:

怎么找到捕获列表? 通过寄存器存储的funcval地址加上偏移。

方法

方法本质上就是普通的函数,方法接收者即为第一个参数。(Go语言传参是值拷贝,不过非字面量有语法糖)

(二者其实都是Function Value)

Defer

Go1.12 的 runtime.g模型表征defer的注册(deferproc),在runtime.deferreturn()语句进行执行。内置defer池,满了再借助堆分配。执行时,不在捕获列表的变量会拷贝到defer结构体进行保存,否则才读取堆的结果。

Go 1.14 采用df变量,按位表示是否需执行和被执行(需要实时条件判断等情况),并使用插入代码的方式进行defer机制。循环中的defer依然需要使用1.12版本的链表机制。

Panic/Recover

_panic链表也在runtime里,和defer链表类似。

panic后执行(非注册)的deferdefer中的panic会指向该panic。执行方式:先标记,后释放,目的是为了终止之前发生的panic。也就是说,执行时再次发生panic,插入新panic,而defer中的_panic不是它,标记对应的defer._panic为已终止。

每个defer函数执行完,会检查panic.recovered,移除已恢复的panic,保留对应的defer的sp和pc后移除该defer,根据保存的信息恢复栈帧和指令地址。

例子(defer链表会被执行完,panic不一致会恢复栈帧):

类型系统

类型元数据

每种类型的类型元数据都是全局唯一的。

接口

<接口类型, 动态类型> -> itab缓存哈希表runtime.itabTableType,用于存储和查询iTab信息,查找哈希值为iface.hash ^ _type.hash

类型断言

类型断言作用在接口值之上。

上方的iTab缓存哈希表,对于不存在的类型断言,也会缓存占坑,设置fun[0]=0表示不存在。

  • 空接口类型断言实现流程:空接口类型断言实质是将eface_type与要匹配的类型进行对比,匹配成功在内存中组装返回值,匹配失败直接清空寄存器,返回默认值。

  • 非空接口类型断言的实质是 iface 中 *itab 的对比。*itab 匹配成功会在内存中组装返回值。匹配失败直接清空寄存器,返回默认值

reflect

使用reflect.TypeOf(t)方法暴露类型元数据。调用参数 t 是编译期处理值拷贝时的临时拷贝的地址。利用ValueOf方法显式把参数指向的变量逃逸到堆上,实现修改,通过Elem得到reflect.Value,结合逃逸的对象进行修改。

GPM

GO scheduler-ProcessOn

Go 语言中,协程对应的数据结构是runtime.g,工作线程对应的数据结构是runtime.m。为避免全局队列调度加锁的性能损失,g分配到本地队列(满了才留在全局队列),由p持有,m执行。

Goexit:退出当前执行的goroutine,但是defer函数还会继续调用 Gosched:让出当前goroutine的执行权限,调度器安排其他等待的任务运行,并在下次某个时候从该位置恢复执行。

Golang内存管理模型的逻辑层次参考了TCMalloc,类比:

buf:内存块单元。是采用链表的集合方式,每个Buf通过 Next进行关联,其中 Data为指向底层开辟出来供用户使用的内存。

TCMalloc则是为每个Thread预分配一块缓存,每个Thread在申请内存时首先会先从这个缓存区ThreadCache申请对应的buf,且所有ThreadCache缓存区(*2单位,类似于buddy system)还共享一个叫CentralCache的中心缓存(加锁交互,起到针对ThreadCache的一层二级缓存作用)。所以为了解决中和大对象的内存申请(三级缓存),TCMalloc依然有一个全局共享内存堆PageHeap。

Page是Golang内存管理与操作系统交互衡量内存容量的基本单元,Golang内存管理内部本身用来给对象存储内存的基本单元是Object:


参考资料: 【幼麟实验室】Golang合辑_哔哩哔哩_bilibili

最后更新于

这有帮助吗?