这是一篇关于 Go 语言实现细节的系列笔记文章,主要内容如下:
- GC Shapes:Go 中类型布局的基本概念是类型的“形状”,这是 Go 垃圾回收器的一个实现细节,通过
unsafe
包暴露。可以使用unsafe.Sizeof
和unsafe.Alignof
查询类型的大小和对齐方式,两者共同构成类型的“布局”。类型的“形状”还记录了其中包含指针的部分,因为垃圾回收器需要知道哪些部分是需要跟踪的指针。 Slices and Strings:
- Go 公开了切片和字符串的布局,切片是一个包含数据指针、长度和容量的结构体,字符串的布局与
[]byte
类似,但有容量限制。 - 可以使用
unsafe
将切片别名到字符串,只要字符串被访问时切片不被修改就很安全,但要注意一些限制,如包含对齐填充的类型不能使用,包含指针的类型可能导致指针不可达,不可比较的类型和接口将通过地址比较。 - 可以使用反射创建动态大小的数组类型,但调用
refl.Interface()
会进行数组的复制,因为数组类型的t.IfaceIndr()
为真,会触发复制操作。
- Go 公开了切片和字符串的布局,切片是一个包含数据指针、长度和容量的结构体,字符串的布局与
The Layout of Go’s Interfaces:
- Go 的接口布局在
runtime2.go
文件中,iface
是通常的接口布局,由itab
(接口表)和数据指针组成,eface
是any
的布局,直接存储类型可以减少类型切换时的指针加载。 - 接口函数调用的生成代码比较复杂,包括检查接口是否为 nil,加载
ITab.Fun
和调用函数等操作。接口的上下转换也有不同的实现,下转换需要动态构建itab
,可能会比较慢且可能导致 panic。 - 类型切换使用类似的缓存机制,PGO 可以用于预填充类型断言缓存,但编译器中未找到相关代码。
- Go 的接口布局在
- Function Pointers:Go 的函数指针布局比较奇怪,通过
runtime.funcval
结构体表示,调用函数时先将函数指针解释为*funcval
并加载fn
字段,捕获的变量通过rdx
寄存器访问。返回函数引用时返回全局函数地址,方法需要创建 thunk 来处理接收者指针的问题,可以通过添加捕获字段或使垃圾回收器忽略rdx
中的指针来解决,但会增加二进制大小或带来安全风险。 - Conclusion:文章旨在记录作者在 Go 语言中遇到的一些细节,供自己参考,同时提到了一些关于
Sizeof
、Alignof
、GC 程序、汇编示例和类型断言缓存更新等方面的注意事项。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。