runtime篇五:slice

本系列代码基于golang1.19 runtime篇一:接口 runtime篇二:通道 runtime篇三:defer runtime篇四:panic runtime篇五:slice 推荐阅读:slice和数组的区别 切片append规则 1. 切片的结构 切片的底层表示是runtime.slice: type slice struct { array unsafe.Pointer len int cap int } 非常简单,只有一个array指针指向底层数组结构,len标记切片长度,cap标记数组容量。 2. 新建一个切片 当我们使用如make([]int, 0)语法来新建一个切片的时候,调用的是runtime.makeslice函数: func makeslice(et *_type, len, cap int) unsafe.Pointer { mem, overflow := math.MulUintptr(et.size, uintptr(cap)) if overflow || mem > maxAlloc || len < 0 || len > cap { // NOTE: Produce a 'len out of range' error instead of a // 'cap out of range' error when someone does make([]T, bignumber)....

August 14, 2022 · 3 min · 李昌

runtime篇三:defer

本系列代码基于golang1.19 runtime篇一:接口 runtime篇二:通道 runtime篇三:defer runtime篇四:panic runtime篇五:slice 1. defer是什么 defer,是一种特殊的机制,在调用普通函数或方法前加上关键字defer,就完成了defer所需要的语法。当defer语句被执行时,跟在defer后面的函数会被延迟执行。直到包含该defer语句的函数执行完毕时,defer后的函数才会被执行,多个defer的执行顺序与声明顺序相反。 对于defer的使用及需要注意的地方,可参考defer用法。这里不再讨论。 在golang runtime中,defer被描述为一个结构体: type _defer struct { started bool // 是否开始执行defer函数 heap bool // 是否分配在堆上 openDefer bool // 是否经过开放编码(open-coded)的优化 sp uintptr // 调用defer时的栈指针 stack pointer pc uintptr // 调用defer函数时的pc值 fn func() // defer关键字传入的函数, 当使用open-coded defer时可为空 _panic *_panic // defer函数中的_panic链表 link *_defer // 在goroutine中的下一个defer,可指向堆或栈 // 如果openDefer为true,则下面的字段将记录具有open-code defer的栈帧和相关的函数。 // 上面的sp将为帧的sp,pc将为defer调用的地址。 fd unsafe....

August 12, 2022 · 5 min · 李昌

runtime篇四:panic

本系列代码基于golang1.19 runtime篇一:接口 runtime篇二:通道 runtime篇三:defer runtime篇四:panic runtime篇五:slice 1. panic的底层结构 panic在runtime中的底层表示是runtime._panic结构体。 type _panic struct { argp unsafe.Pointer // 指向defer调用时参数的指针 arg any // panic参数 link *_panic // 连接到更早的_panic pc uintptr // 程序计数器 sp unsafe.Pointer // 栈指针 recovered bool // 当前panic是否被recover恢复 aborted bool // 当前panic是否被中止 goexit bool // 是否调用了runtime.Goexit } 类似于_defer,panic也被组织成链表结构,多个panic通过link字段连接成一个链表。 在_panic结构体中,pc、sp、goexit三个字段是为了修复runtime.Goexit带来的问题引入的[1]. 2. 调用panic 在函数中调用panic时,底层会调用runtime.gopanic,其源码如下: func gopanic(e any) { gp := getg() // 获取当前g // ....

August 12, 2022 · 7 min · 李昌

runtime篇二:通道

本系列代码基于golang1.19 runtime篇一:接口 runtime篇二:通道 runtime篇三:defer runtime篇四:panic runtime篇五:slice 1. chan的结构 一个channel长这样: 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 // chan中元素大小 closed uint32 // 是否关闭 elemtype *_type // element type sendx uint // send index recvx uint // receive index recvq waitq // list of recv waiters sendq waitq // list of send waiters lock mutex } channel的字段中,主要可以分为三部分:...

August 5, 2022 · 7 min · 李昌

runtime篇一:接口

本系列代码基于golang1.19 runtime篇一:接口 runtime篇二:通道 runtime篇三:defer runtime篇四:panic runtime篇五:slice 1. 接口的内部结构 type iface struct { tab *itab data unsafe.Pointer } 一个接口是一个iface结构体,其中包含一个itab指针和一个unsafe.Pointer。 概念上讲一个接口的值,接口值,由两个部分组成,一个具体的类型和那个类型的值。它们被称为接口的动态类型和动态值。 一个itab可以表示一个接口的类型和赋给这个接口的实体类型,即为接口的动态类型,而data所指向的unsafe.Pointer则指向接口的动态值。 近距离来看itab: type itab struct { inter *interfacetype _type *_type hash uint32 // copy of _type.hash. Used for type switches. _ [4]byte fun [1]uintptr // variable sized. fun[0]==0 means _type does not implement inter. } 其中inter字段描述了接口的类型,_type字段描述了实体类型,hash字段是类型哈希,用于类型匹配,fun字段放置和接口方法对应的具体数据类型的方法地址。 再来看interfacetype type interfacetype struct { typ _type pkgpath name mhdr []imethod } 其中typ和itab中的_type为同一个值,pkgpath则存储了接口的包名,mhdr则表示接口所定义的函数列表。...

August 4, 2022 · 9 min · 李昌

切片append规则

大约2021年8月份,go社区对切片容量增长的方式进行了一次调整。具体讨论可见:https://groups.google.com/g/golang-nuts/c/UaVlMQ8Nz3o 1. 之前的增长规则 先看源码 runtime/slice.go func growslice(et *_type, old slice, cap int) slice { // 省略部分条件检查 // ... newcap := old.cap doublecap := newcap + newcap if cap > doublecap { newcap = cap } else { if old.cap < 1024 { newcap = doublecap } else { // Check 0 < newcap to detect overflow // and prevent an infinite loop. for 0 < newcap && newcap < cap { newcap += newcap / 4 } // Set newcap to the requested cap when // the newcap calculation overflowed....

March 19, 2022 · 2 min · 李昌