上卷 程序设计
中卷 标准库
- bufio 1.18
- bytes 1.18
- io 1.18
- container 1.18
- encoding 1.18
- crypto 1.18
- hash 1.18
- index 1.18
- sort 1.18
- context 1.18
- database 1.18
- connection
- query
- queryrow
- exec
- prepare
- transaction
- scan & null
- context
- tcp
- udp
- http
- server
- handler
- client
- h2、tls
- url
- rpc
- exec
- signal
- embed 1.18
- plugin 1.18
- reflect 1.18
- runtime 1.18
- KeepAlived
- ReadMemStats
- SetFinalizer
- Stack
- sync 1.18
- atomic
- mutex
- rwmutex
- waitgroup
- cond
- once
- map
- pool
- copycheck
- nocopy
- unsafe 1.18
- fmt 1.18
- log 1.18
- math 1.18
- time 1.18
- timer
下卷 运行时
源码剖析
附录
文章来源于网络收集而来,版权归原创者所有,如有侵权请及时联系!
2.2 引用类型
特指 slice
、 map
、 channel
这三种预定义类型。
相比数字、数组等简单类型,引用类型有更复杂的内部结构。除分配内存外,还需初始化一系列属性,诸如指针、长度,甚至哈希分布、数据队列等。
从语言规范看,没有传统意义上的值类型和引用类型。只所以如此表述,除历史原因(早期文档)外,还因为它们与
make
函数的特殊关系,且被编译器和运行时特别对待(优化)。
slice array +-----------+ +-----//-----+ | array -|-----> | ... ... | +-----------+ +-----//-----+ | len | +-----------+ | cap | +-----------+ |<-- new -->| |<------------- make ----------->|
以上图 slice
结构为例,由头和底层数组两部分组成。
new
:按类型大小分配零值内存,返回指针。make
:转为类型构造函数(或指令),完成内部初始化。
实际上,底层数组不是切片的必然组成部分。详情参考后续章节。
函数 new
只关心类型长度( sizeof
),不涉及内部结构和逻辑。
func main() { s := *new([]int) // slice: { ptr, len, cap } m := *new(map[string]int) // map: ptr c := *new(chan int) // chan: ptr println(unsafe.Sizeof(s)) // 24 println(unsafe.Sizeof(m)) // 8 println(unsafe.Sizeof(c)) // 8 } /* (gdb) x/3xg &s ; 没有底层数组。 0xc000036740: 0x0000000000000000 0x0000000000000000 0xc000036750: 0x0000000000000000 (gdb) x/xg &m ; 仅指针。 0xc000036708: 0x0000000000000000 (gdb) x/xg &c 0xc000036710: 0x0000000000000000 */ /* $ go build -gcflags "-N" $ go tool objdump -S -s "main\.main" ./test func main() { ; 不涉及内部分配和逻辑处理。 ... m := *new(map[string]int) MOVQ $0x0, 0x20(SP) MOVQ 0x20(SP), AX MOVQ AX, 0x8(SP) c := *new(chan int) MOVQ $0x0, 0x18(SP) MOVQ 0x18(SP), AX MOVQ AX, 0x10(SP) } */
虽然 new
的行为很像 malloc
,但优先在栈上分配。
func main() { p := new(int) *p = 100 } /* $ go build -gcflags "-N" $ go tool objdump -S -s "main\.main" ./test func main() { p := new(int) MOVQ $0x0, 0(SP) LEAQ 0(SP), AX MOVQ AX, 0x8(SP) *p = 100 MOVQ $0x64, 0(SP) } */
编译器将 make
翻译成 makeslice
、 makemap
之类的构造函数调用(或等价指令)。
相关实现细节,可参考下卷有关
map
的源码剖析章节。
func main() { s := make([]int, 10, 100) println(unsafe.Sizeof(s)) // 24 } /* (gdb) x/3xg &s ; 分配底层数组,并初始化 len 和 cap 字段。 0xc000032758: 0x000000c000032438 0x000000000000000a 0xc000032768: 0x0000000000000064 */
func main() { s := make([]int, 10000) s[0] = 1 m := make(map[string]int, 10) m["a"] = 1 } /* $ go build -gcflags "-N" $ go tool objdump -S -s "main\.main" ./test func main() { s := make([]int, 10000) CALL runtime.makeslice(SB) m := make(map[string]int, 10) CALL runtime.makemap(SB) */
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论