为什么分析map
在计算机编程语言当中,用的最多的数据结构估计就是map。map以他近乎o(1)的查找效率和修改效率让他在大多数场景下都比较受青睐。map的常规的实现方式都是hash+其他数据结构,如java是hash+红黑树,而我现在即将要分析的go的实现方式是hash+链表。我会分析map的几乎每段代码,并且在我的GitHub可以查看到我的分析,注释十分详尽,欢迎批评指正。我的打算是把一些常用的数据结构都分析一遍,如果有志同道合的人,可以联系我。
我的环境
为了给那些感兴趣看源码分析我的博客的同学阅读得更加清晰,列举了我的环境: 1. go1.14.7 amd64; 2. windows和mac接口,linux没有测试,估计没问题 ; 3. goland,目前最好的golang ide,虽然笔者用起来感觉还是不太完美; 4. 着重分析map[string]string,其他类型的源码变化不大 ;
本篇重点
1.go的map和java的map有区别,go中是一个关键字,而java确实可以直接查看源码,那么如何分析go的map源码?
2.调试过程当中,go充斥着大量指针,如何知道指针指向的内容?map的最小单位bmap除了查看tophash,怎么查看里面的其他隐藏字段?
3.go的map源码为何给不同的key的类型设计不同的实现?
4.其他一些查看源码的小技巧:内存对齐、指针偏移、类型大小等等
如何找到map对应的源码
go的map的结构体是hmap,代码位于runtime/map.go,当编译器编译你申明使用map的源码时候,其实是使用了runtime/map*.go,后面对map的增删改查都是执行该代码,故要弄懂map的底层原理只需要分析该源码,也可以断点调试。 我下面有个自己测试的源码,注意我编译之后查看,就可以发现端倪:
package mainimport "fmt"func main() {m1 := make(map[string]string)fmt.Println(m1)m2 := make(map[string]string, 8)fmt.Println(m2)m3 := make(map[string]string, 9)fmt.Println(m3)m4 := map[string]string{}fmt.Println(m4)m3["1"] = "2"for k, v := range m3 {fmt.Println(k)fmt.Println(v)}v1 := m3["1"]fmt.Println(v1)if v2, ok := m3["1"]; ok {fmt.Println(v2)}
}
上面代码有几种不同申明map的方式,对应runtime/map.go也不同。还有赋值和遍历查找的代码。 下面编译得到汇编指令文件,执行
go tool compile -N -l -S main.go > main.txt
生成了汇编指令文件:
"".main STEXT size=1891 args=0x0 locals=0x2880x0000 00000 (main.go:5) TEXT "".main(SB), ABIInternal, $648-00x0000 00000 (main.go:5) MOVQ TLS, CX0x0009 00009 (main.go:5) PCDATA $0, $-20x0009 00009 (main.go:5) MOVQ (CX)(TLS*2), CX0x0010 00016 (main.go:5) PCDATA $0, $-10x0010 00016 (main.go:5) LEAQ -520(SP), AX0x0018 00024 (main.go:5) CMPQ AX, 16(CX)0x001c 00028 (main.go:5) PCDATA $0, $-20x001c 00028 (main.go:5) JLS 18810x0022 00034 (main.go:5) PCDATA $0, $-10x0022 00034 (main.go:5) SUBQ $648, SP0x0029 00041 (main.go:5) MOVQ BP, 640(SP)0x0031 00049 (main.go:5) LEAQ 640(SP), BP0x0039 00057 (main.go:5) PCDATA $0, $-20x0039 00057 (main.go:5) PCDATA $1, $-20x0039 00057 (main.go:5) FUNCDATA $0, gclocals·ad6397d3d33bbb6a31b1320349e23274(SB)0x0039 00057 (main.go:5) FUNCDATA $1, gclocals·1bbf1965a34fa71a63e073b1cf2d752a(SB)0x0039 00057 (main.go:5) FUNCDATA $2, gclocals·658035074399f748be6dd39603d5113c(SB)0x0039 00057 (main.go:5) FUNCDATA $3, "".main.stkobj(SB)0x0039 00057 (main.go:6) PCDATA $0, $00x0039 00057 (main.go:6) PCDATA $1, $00x0039 00057 (main.go:6) CALL runtime.makemap_small(SB)0x003e 00062 (main.go:6) PCDATA $0, $10x003e 00062 (main.go:6) MOVQ (SP), AX0x0042 00066 (main.go:6) MOVQ AX, "".m1+80(SP)0x0047 00071 (main.go:7) PCDATA $0, $00x0047 00071 (main.go:7) PCDATA $1, $10x0047 00071 (main.go:7) MOVQ AX, ""..autotmp_10+200(SP)0x004f 00079 (main.go:7) PCDATA $1, $20x004f 00079 (main.go:7) XORPS X0, X00x0052 00082 (main.go:7) MOVUPS X0, ""..autotmp_9+272(SP)0x005a 00090 (main.go:7) PCDATA $0, $10x005a 00090 (main.go:7) PCDATA $1, $10x005a 00090 (main.go:7) LEAQ ""..autotmp_9+272(SP), AX0x0062 00098 (main.go:7) MOVQ AX, ""..autotmp_15+192(SP)0x006a 00106 (main.go:7) TESTB AL, (AX)0x006c 00108 (main.go:7) PCDATA $0, $20x006c 00108 (main.go:7) PCDATA $1, $00x006c 00108 (main.go:7) MOVQ ""..autotmp_10+200(SP), CX0x0074 00116 (main.go:7) PCDATA $0, $30x0074 00116 (main.go:7) LEAQ type.map[string]string(SB), DX0x007b 00123 (main.go:7) PCDATA $0, $20x007b 00123 (main.go:7) MOVQ DX, ""..autotmp_9+272(SP)0x0083 00131 (main.go:7) PCDATA $0, $10x0083 00131 (main.go:7) MOVQ CX, ""..autotmp_9+280(SP)0x008b 00139 (main.go:7) TESTB AL, (AX)0x008d 00141 (main.go:7) JMP 1430x008f 00143 (main.go:7) MOVQ AX, ""..autotmp_14+520(SP)0x0097 00151 (main.go:7) MOVQ $1, ""..autotmp_14+528(SP)0x00a3 00163 (main.go:7) MOVQ $1, ""..autotmp_14+536(SP)0x00af 00175 (main.go:7) PCDATA $0, $00x00af 00175 (main.go:7) MOVQ AX, (SP)0x00b3 00179 (main.go:7) MOVQ $1, 8(SP)0x00bc 00188 (main.go:7) MOVQ $1, 16(SP)0x00c5 00197 (main.go:7) CALL fmt.Println(SB)0x00ca 00202 (main.go:8) CALL runtime.makemap_small(SB)0x00cf 00207 (main.go:8) PCDATA $0, $10x00cf 00207 (main.go:8) MOVQ (SP), AX0x00d3 00211 (main.go:8) MOVQ AX, "".m2+72(SP)0x00d8 00216 (main.go:9) PCDATA $0, $00x00d8 00216 (main.go:9) PCDATA $1, $10x00d8 00216 (main.go:9) MOVQ AX, ""..autotmp_10+200(SP)0x00e0 00224 (main.go:9) PCDATA $1, $20x00e0 00224 (main.go:9) XORPS X0, X00x00e3 00227 (main.go:9) MOVUPS X0, ""..autotmp_9+272(SP)0x00eb 00235 (main.go:9) PCDATA $0, $10x00eb 00235 (main.go:9) PCDATA $1, $10x00eb 00235 (main.go:9) LEAQ ""..autotmp_9+272(SP), AX0x00f3 00243 (main.go:9) MOVQ AX, ""..autotmp_17+184(SP)0x00fb 00251 (main.go:9) TESTB AL, (AX)0x00fd 00253 (main.go:9) PCDATA $0, $20x00fd 00253 (main.go:9) PCDATA $1, $00x00fd 00253 (main.go:9) MOVQ ""..autotmp_10+200(SP), CX0x0105 00261 (main.go:9) PCDATA $0, $30x0105 00261 (main.go:9) LEAQ type.map[string]string(SB), DX0x010c 00268 (main.go:9) PCDATA $0, $20x010c 00268 (main.go:9) MOVQ DX, ""..autotmp_9+272(SP)0x0114 00276 (main.go:9) PCDATA $0, $10x0114 00276 (main.go:9) MOVQ CX, ""..autotmp_9+280(SP)0x011c 00284 (main.go:9) TESTB AL, (AX)0x011e 00286 (main.go:9) JMP 2880x0120 00288 (main.go:9) MOVQ AX, ""..autotmp_16+496(SP)0x0128 00296 (main.go:9) MOVQ $1, ""..autotmp_16+504(SP)0x0134 00308 (main.go:9) MOVQ $1, ""..autotmp_16+512(SP)0x0140 00320 (main.go:9) PCDATA $0, $00x0140 00320 (main.go:9) MOVQ AX, (SP)0x0144 00324 (main.go:9) MOVQ $1, 8(SP)0x014d 00333 (main.go:9) MOVQ $1, 16(SP)0x0156 00342 (main.go:9) CALL fmt.Println(SB)0x015b 00347 (main.go:10) PCDATA $0, $10x015b 00347 (main.go:10) LEAQ type.map[string]string(SB), AX0x0162 00354 (main.go:10) PCDATA $0, $00x0162 00354 (main.go:10) MOVQ AX, (SP)0x0166 00358 (main.go:10) MOVQ $9, 8(SP)0x016f 00367 (main.go:10) MOVQ $0, 16(SP)0x0178 00376 (main.go:10) CALL runtime.makemap(SB)0x017d 00381 (main.go:10) PCDATA $0, $10x017d 00381 (main.go:10) MOVQ 24(SP), AX0x0182 00386 (main.go:10) PCDATA $1, $30x0182 00386 (main.go:10) MOVQ AX, "".m3+64(SP)0x0187 00391 (main.go:11) PCDATA $0, $00x0187 00391 (main.go:11) PCDATA $1, $40x0187 00391 (main.go:11) MOVQ AX, ""..autotmp_10+200(SP)0x018f 00399 (main.go:11) PCDATA $1, $50x018f 00399 (main.go:11) XORPS X0, X00x0192 00402 (main.go:11) MOVUPS X0, ""..autotmp_9+272(SP)0x019a 00410 (main.go:11) PCDATA $0, $10x019a 00410 (main.go:11) PCDATA $1, $40x019a 00410 (main.go:11) LEAQ ""..autotmp_9+272(SP), AX0x01a2 00418 (main.go:11) MOVQ AX, ""..autotmp_19+176(SP)0x01aa 00426 (main.go:11) TESTB AL, (AX)0x01ac 00428 (main.go:11) PCDATA $0, $20x01ac 00428 (main.go:11) PCDATA $1, $30x01ac 00428 (main.go:11) MOVQ ""..autotmp_10+200(SP), CX0x01b4 00436 (main.go:11) PCDATA $0, $30x01b4 00436 (main.go:11) LEAQ type.map[string]string(SB), DX0x01bb 00443 (main.go:11) PCDATA $0, $20x01bb 00443 (main.go:11) MOVQ DX, ""..autotmp_9+272(SP)0x01c3 00451 (main.go:11) PCDATA $0, $10x01c3 00451 (main.go:11) MOVQ CX, ""..autotmp_9+280(SP)0x01cb 00459 (main.go:11) TESTB AL, (AX)0x01cd 00461 (main.go:11) JMP 4630x01cf 00463 (main.go:11) MOVQ AX, ""..autotmp_18+472(SP)0x01d7 00471 (main.go:11) MOVQ $1, ""..autotmp_18+480(SP)0x01e3 00483 (main.go:11) MOVQ $1, ""..autotmp_18+488(SP)0x01ef 00495 (main.go:11) PCDATA $0, $00x01ef 00495 (main.go:11) MOVQ AX, (SP)0x01f3 00499 (main.go:11) MOVQ $1, 8(SP)0x01fc 00508 (main.go:11) MOVQ $1, 16(SP)0x0205 00517 (main.go:11) CALL fmt.Println(SB)0x020a 00522 (main.go:12) CALL runtime.makemap_small(SB)0x020f 00527 (main.go:12) PCDATA $0, $10x020f 00527 (main.go:12) MOVQ (SP), AX0x0213 00531 (main.go:12) PCDATA $0, $00x0213 00531 (main.go:12) PCDATA $1, $60x0213 00531 (main.go:12) MOVQ AX, "".m4+56(SP)0x0218 00536 (main.go:13) PCDATA $0, $10x0218 00536 (main.go:13) PCDATA $1, $30x0218 00536 (main.go:13) MOVQ "".m4+56(SP), AX0x021d 00541 (main.go:13) PCDATA $0, $00x021d 00541 (main.go:13) PCDATA $1, $40x021d 00541 (main.go:13) MOVQ AX, ""..autotmp_10+200(SP)0x0225 00549 (main.go:13) PCDATA $1, $50x0225 00549 (main.go:13) XORPS X0, X00x0228 00552 (main.go:13) MOVUPS X0, ""..autotmp_9+272(SP)0x0230 00560 (main.go:13) PCDATA $0, $10x0230 00560 (main.go:13) PCDATA $1, $40x0230 00560 (main.go:13) LEAQ ""..autotmp_9+272(SP), AX0x0238 00568 (main.go:13) MOVQ AX, ""..autotmp_23+168(SP)0x0240 00576 (main.go:13) TESTB AL, (AX)0x0242 00578 (main.go:13) PCDATA $0, $20x0242 00578 (main.go:13) PCDATA $1, $30x0242 00578 (main.go:13) MOVQ ""..autotmp_10+200(SP), CX0x024a 00586 (main.go:13) PCDATA $0, $30x024a 00586 (main.go:13) LEAQ type.map[string]string(SB), DX0x0251 00593 (main.go:13) PCDATA $0, $20x0251 00593 (main.go:13) MOVQ DX, ""..autotmp_9+272(SP)0x0259 00601 (main.go:13) PCDATA $0, $10x0259 00601 (main.go:13) MOVQ CX, ""..autotmp_9+280(SP)0x0261 00609 (main.go:13) TESTB AL, (AX)0x0263 00611 (main.go:13) JMP 6130x0265 00613 (main.go:13) MOVQ AX, ""..autotmp_22+448(SP)0x026d 00621 (main.go:13) MOVQ $1, ""..autotmp_22+456(SP)0x0279 00633 (main.go:13) MOVQ $1, ""..autotmp_22+464(SP)0x0285 00645 (main.go:13) PCDATA $0, $00x0285 00645 (main.go:13) MOVQ AX, (SP)0x0289 00649 (main.go:13) MOVQ $1, 8(SP)0x0292 00658 (main.go:13) MOVQ $1, 16(SP)0x029b 00667 (main.go:13) CALL fmt.Println(SB)0x02a0 00672 (main.go:14) PCDATA $0, $10x02a0 00672 (main.go:14) LEAQ type.map[string]string(SB), AX0x02a7 00679 (main.go:14) PCDATA $0, $00x02a7 00679 (main.go:14) MOVQ AX, (SP)0x02ab 00683 (main.go:14) PCDATA $0, $10x02ab 00683 (main.go:14) MOVQ "".m3+64(SP), AX0x02b0 00688 (main.go:14) PCDATA $0, $00x02b0 00688 (main.go:14) MOVQ AX, 8(SP)0x02b5 00693 (main.go:14) PCDATA $0, $10x02b5 00693 (main.go:14) LEAQ go.string."1"(SB), AX0x02bc 00700 (main.go:14) PCDATA $0, $00x02bc 00700 (main.go:14) MOVQ AX, 16(SP)0x02c1 00705 (main.go:14) MOVQ $1, 24(SP)0x02ca 00714 (main.go:14) CALL runtime.mapassign_faststr(SB)0x02cf 00719 (main.go:14) PCDATA $0, $40x02cf 00719 (main.go:14) MOVQ 32(SP), DI0x02d4 00724 (main.go:14) MOVQ DI, ""..autotmp_24+160(SP)0x02dc 00732 (main.go:14) TESTB AL, (DI)0x02de 00734 (main.go:14) MOVQ $1, 8(DI)0x02e6 00742 (main.go:14) PCDATA $0, $-20x02e6 00742 (main.go:14) PCDATA $1, $-20x02e6 00742 (main.go:14) CMPL runtime.writeBarrier(SB), $00x02ed 00749 (main.go:14) JEQ 7560x02ef 00751 (main.go:14) JMP 18640x02f4 00756 (main.go:14) LEAQ go.string."2"(SB), AX0x02fb 00763 (main.go:14) MOVQ AX, (DI)0x02fe 00766 (main.go:14) JMP 7680x0300 00768 (main.go:15) PCDATA $0, $10x0300 00768 (main.go:15) PCDATA $1, $30x0300 00768 (main.go:15) MOVQ "".m3+64(SP), AX0x0305 00773 (main.go:15) PCDATA $0, $00x0305 00773 (main.go:15) PCDATA $1, $40x0305 00773 (main.go:15) MOVQ AX, ""..autotmp_10+200(SP)0x030d 00781 (main.go:15) PCDATA $0, $40x030d 00781 (main.go:15) PCDATA $1, $70x030d 00781 (main.go:15) LEAQ ""..autotmp_11+544(SP), DI0x0315 00789 (main.go:15) XORPS X0, X00x0318 00792 (main.go:15) PCDATA $0, $00x0318 00792 (main.go:15) LEAQ -32(DI), DI0x031c 00796 (main.go:15) DUFFZERO $2730x032f 00815 (main.go:15) PCDATA $0, $10x032f 00815 (main.go:15) LEAQ type.map[string]string(SB), AX0x0336 00822 (main.go:15) PCDATA $0, $00x0336 00822 (main.go:15) MOVQ AX, (SP)0x033a 00826 (main.go:15) PCDATA $0, $10x033a 00826 (main.go:15) PCDATA $1, $80x033a 00826 (main.go:15) MOVQ ""..autotmp_10+200(SP), AX0x0342 00834 (main.go:15) PCDATA $0, $00x0342 00834 (main.go:15) MOVQ AX, 8(SP)0x0347 00839 (main.go:15) PCDATA $0, $10x0347 00839 (main.go:15) LEAQ ""..autotmp_11+544(SP), AX0x034f 00847 (main.go:15) PCDATA $0, $00x034f 00847 (main.go:15) MOVQ AX, 16(SP)0x0354 00852 (main.go:15) CALL runtime.mapiterinit(SB)0x0359 00857 (main.go:15) JMP 8590x035b 00859 (main.go:15) CMPQ ""..autotmp_11+544(SP), $00x0364 00868 (main.go:15) JNE 8750x0366 00870 (main.go:15) JMP 13290x036b 00875 (main.go:15) PCDATA $0, $10x036b 00875 (main.go:15) MOVQ ""..autotmp_11+552(SP), AX0x0373 00883 (main.go:15) TESTB AL, (AX)0x0375 00885 (main.go:15) MOVQ 8(AX), CX0x0379 00889 (main.go:15) MOVQ (AX), AX0x037c 00892 (main.go:15) PCDATA $0, $00x037c 00892 (main.go:15) PCDATA $1, $90x037c 00892 (main.go:15) MOVQ AX, ""..autotmp_25+288(SP)0x0384 00900 (main.go:15) MOVQ CX, ""..autotmp_25+296(SP)0x038c 00908 (main.go:15) PCDATA $0, $10x038c 00908 (main.go:15) MOVQ ""..autotmp_11+544(SP), AX0x0394 00916 (main.go:15) TESTB AL, (AX)0x0396 00918 (main.go:15) MOVQ 8(AX), CX0x039a 00922 (main.go:15) MOVQ (AX), AX0x039d 00925 (main.go:15) PCDATA $0, $00x039d 00925 (main.go:15) PCDATA $1, $100x039d 00925 (main.go:15) MOVQ AX, "".k+256(SP)0x03a5 00933 (main.go:15) MOVQ CX, "".k+264(SP)0x03ad 00941 (main.go:15) MOVQ ""..autotmp_25+296(SP), AX0x03b5 00949 (main.go:15) PCDATA $0, $50x03b5 00949 (main.go:15) PCDATA $1, $110x03b5 00949 (main.go:15) MOVQ ""..autotmp_25+288(SP), CX0x03bd 00957 (main.go:15) PCDATA $0, $00x03bd 00957 (main.go:15) PCDATA $1, $120x03bd 00957 (main.go:15) MOVQ CX, "".v+240(SP)0x03c5 00965 (main.go:15) MOVQ AX, "".v+248(SP)0x03cd 00973 (main.go:16) MOVQ "".k+264(SP), AX0x03d5 00981 (main.go:16) PCDATA $0, $50x03d5 00981 (main.go:16) PCDATA $1, $130x03d5 00981 (main.go:16) MOVQ "".k+256(SP), CX0x03dd 00989 (main.go:16) PCDATA $0, $00x03dd 00989 (main.go:16) MOVQ CX, (SP)0x03e1 00993 (main.go:16) MOVQ AX, 8(SP)0x03e6 00998 (main.go:16) CALL runtime.convTstring(SB)0x03eb 01003 (main.go:16) PCDATA $0, $10x03eb 01003 (main.go:16) MOVQ 16(SP), AX0x03f0 01008 (main.go:16) PCDATA $0, $00x03f0 01008 (main.go:16) PCDATA $1, $140x03f0 01008 (main.go:16) MOVQ AX, ""..autotmp_26+152(SP)0x03f8 01016 (main.go:16) PCDATA $1, $150x03f8 01016 (main.go:16) XORPS X0, X00x03fb 01019 (main.go:16) MOVUPS X0, ""..autotmp_9+272(SP)0x0403 01027 (main.go:16) PCDATA $0, $10x0403 01027 (main.go:16) PCDATA $1, $140x0403 01027 (main.go:16) LEAQ ""..autotmp_9+272(SP), AX0x040b 01035 (main.go:16) MOVQ AX, ""..autotmp_28+144(SP)0x0413 01043 (main.go:16) TESTB AL, (AX)0x0415 01045 (main.go:16) PCDATA $0, $20x0415 01045 (main.go:16) PCDATA $1, $130x0415 01045 (main.go:16) MOVQ ""..autotmp_26+152(SP), CX0x041d 01053 (main.go:16) PCDATA $0, $30x041d 01053 (main.go:16) LEAQ type.string(SB), DX0x0424 01060 (main.go:16) PCDATA $0, $20x0424 01060 (main.go:16) MOVQ DX, ""..autotmp_9+272(SP)0x042c 01068 (main.go:16) PCDATA $0, $10x042c 01068 (main.go:16) MOVQ CX, ""..autotmp_9+280(SP)0x0434 01076 (main.go:16) TESTB AL, (AX)0x0436 01078 (main.go:16) JMP 10800x0438 01080 (main.go:16) MOVQ AX, ""..autotmp_27+424(SP)0x0440 01088 (main.go:16) MOVQ $1, ""..autotmp_27+432(SP)0x044c 01100 (main.go:16) MOVQ $1, ""..autotmp_27+440(SP)0x0458 01112 (main.go:16) PCDATA $0, $00x0458 01112 (main.go:16) MOVQ AX, (SP)0x045c 01116 (main.go:16) MOVQ $1, 8(SP)0x0465 01125 (main.go:16) MOVQ $1, 16(SP)0x046e 01134 (main.go:16) CALL fmt.Println(SB)0x0473 01139 (main.go:17) MOVQ "".v+248(SP), AX0x047b 01147 (main.go:17) PCDATA $0, $50x047b 01147 (main.go:17) PCDATA $1, $80x047b 01147 (main.go:17) MOVQ "".v+240(SP), CX0x0483 01155 (main.go:17) PCDATA $0, $00x0483 01155 (main.go:17) MOVQ CX, (SP)0x0487 01159 (main.go:17) MOVQ AX, 8(SP)0x048c 01164 (main.go:17) CALL runtime.convTstring(SB)0x0491 01169 (main.go:17) PCDATA $0, $10x0491 01169 (main.go:17) MOVQ 16(SP), AX0x0496 01174 (main.go:17) PCDATA $0, $00x0496 01174 (main.go:17) PCDATA $1, $160x0496 01174 (main.go:17) MOVQ AX, ""..autotmp_29+136(SP)0x049e 01182 (main.go:17) PCDATA $1, $170x049e 01182 (main.go:17) XORPS X0, X00x04a1 01185 (main.go:17) MOVUPS X0, ""..autotmp_9+272(SP)0x04a9 01193 (main.go:17) PCDATA $0, $10x04a9 01193 (main.go:17) PCDATA $1, $160x04a9 01193 (main.go:17) LEAQ ""..autotmp_9+272(SP), AX0x04b1 01201 (main.go:17) MOVQ AX, ""..autotmp_31+128(SP)0x04b9 01209 (main.go:17) TESTB AL, (AX)0x04bb 01211 (main.go:17) PCDATA $0, $20x04bb 01211 (main.go:17) PCDATA $1, $80x04bb 01211 (main.go:17) MOVQ ""..autotmp_29+136(SP), CX0x04c3 01219 (main.go:17) PCDATA $0, $30x04c3 01219 (main.go:17) LEAQ type.string(SB), DX0x04ca 01226 (main.go:17) PCDATA $0, $20x04ca 01226 (main.go:17) MOVQ DX, ""..autotmp_9+272(SP)0x04d2 01234 (main.go:17) PCDATA $0, $10x04d2 01234 (main.go:17) MOVQ CX, ""..autotmp_9+280(SP)0x04da 01242 (main.go:17) TESTB AL, (AX)0x04dc 01244 (main.go:17) JMP 12460x04de 01246 (main.go:17) MOVQ AX, ""..autotmp_30+400(SP)0x04e6 01254 (main.go:17) MOVQ $1, ""..autotmp_30+408(SP)0x04f2 01266 (main.go:17) MOVQ $1, ""..autotmp_30+416(SP)0x04fe 01278 (main.go:17) PCDATA $0, $00x04fe 01278 (main.go:17) MOVQ AX, (SP)0x0502 01282 (main.go:17) MOVQ $1, 8(SP)0x050b 01291 (main.go:17) MOVQ $1, 16(SP)0x0514 01300 (main.go:17) CALL fmt.Println(SB)0x0519 01305 (main.go:17) JMP 13070x051b 01307 (main.go:15) PCDATA $0, $10x051b 01307 (main.go:15) LEAQ ""..autotmp_11+544(SP), AX0x0523 01315 (main.go:15) PCDATA $0, $00x0523 01315 (main.go:15) MOVQ AX, (SP)0x0527 01319 (main.go:15) CALL runtime.mapiternext(SB)0x052c 01324 (main.go:15) JMP 8590x0531 01329 (main.go:19) PCDATA $0, $10x0531 01329 (main.go:19) PCDATA $1, $30x0531 01329 (main.go:19) LEAQ type.map[string]string(SB), AX0x0538 01336 (main.go:19) PCDATA $0, $00x0538 01336 (main.go:19) MOVQ AX, (SP)0x053c 01340 (main.go:19) PCDATA $0, $10x053c 01340 (main.go:19) MOVQ "".m3+64(SP), AX0x0541 01345 (main.go:19) PCDATA $0, $00x0541 01345 (main.go:19) MOVQ AX, 8(SP)0x0546 01350 (main.go:19) PCDATA $0, $10x0546 01350 (main.go:19) LEAQ go.string."1"(SB), AX0x054d 01357 (main.go:19) PCDATA $0, $00x054d 01357 (main.go:19) MOVQ AX, 16(SP)0x0552 01362 (main.go:19) MOVQ $1, 24(SP)0x055b 01371 (main.go:19) CALL runtime.mapaccess1_faststr(SB)0x0560 01376 (main.go:19) PCDATA $0, $10x0560 01376 (main.go:19) MOVQ 32(SP), AX0x0565 01381 (main.go:19) PCDATA $0, $20x0565 01381 (main.go:19) MOVQ (AX), CX0x0568 01384 (main.go:19) PCDATA $0, $50x0568 01384 (main.go:19) MOVQ 8(AX), AX0x056c 01388 (main.go:19) MOVQ CX, "".v1+224(SP)0x0574 01396 (main.go:19) MOVQ AX, "".v1+232(SP)0x057c 01404 (main.go:20) PCDATA $0, $00x057c 01404 (main.go:20) MOVQ CX, (SP)0x0580 01408 (main.go:20) MOVQ AX, 8(SP)0x0585 01413 (main.go:20) CALL runtime.convTstring(SB)0x058a 01418 (main.go:20) PCDATA $0, $10x058a 01418 (main.go:20) MOVQ 16(SP), AX0x058f 01423 (main.go:20) PCDATA $0, $00x058f 01423 (main.go:20) PCDATA $1, $180x058f 01423 (main.go:20) MOVQ AX, ""..autotmp_32+120(SP)0x0594 01428 (main.go:20) PCDATA $1, $190x0594 01428 (main.go:20) XORPS X0, X00x0597 01431 (main.go:20) MOVUPS X0, ""..autotmp_9+272(SP)0x059f 01439 (main.go:20) PCDATA $0, $10x059f 01439 (main.go:20) PCDATA $1, $180x059f 01439 (main.go:20) LEAQ ""..autotmp_9+272(SP), AX0x05a7 01447 (main.go:20) MOVQ AX, ""..autotmp_34+112(SP)0x05ac 01452 (main.go:20) TESTB AL, (AX)0x05ae 01454 (main.go:20) PCDATA $0, $20x05ae 01454 (main.go:20) PCDATA $1, $30x05ae 01454 (main.go:20) MOVQ ""..autotmp_32+120(SP), CX0x05b3 01459 (main.go:20) PCDATA $0, $30x05b3 01459 (main.go:20) LEAQ type.string(SB), DX0x05ba 01466 (main.go:20) PCDATA $0, $20x05ba 01466 (main.go:20) MOVQ DX, ""..autotmp_9+272(SP)0x05c2 01474 (main.go:20) PCDATA $0, $10x05c2 01474 (main.go:20) MOVQ CX, ""..autotmp_9+280(SP)0x05ca 01482 (main.go:20) TESTB AL, (AX)0x05cc 01484 (main.go:20) JMP 14860x05ce 01486 (main.go:20) MOVQ AX, ""..autotmp_33+376(SP)0x05d6 01494 (main.go:20) MOVQ $1, ""..autotmp_33+384(SP)0x05e2 01506 (main.go:20) MOVQ $1, ""..autotmp_33+392(SP)0x05ee 01518 (main.go:20) PCDATA $0, $00x05ee 01518 (main.go:20) MOVQ AX, (SP)0x05f2 01522 (main.go:20) MOVQ $1, 8(SP)0x05fb 01531 (main.go:20) MOVQ $1, 16(SP)0x0604 01540 (main.go:20) CALL fmt.Println(SB)0x0609 01545 (main.go:21) XORPS X0, X00x060c 01548 (main.go:21) MOVUPS X0, ""..autotmp_12+336(SP)0x0614 01556 (main.go:21) PCDATA $0, $10x0614 01556 (main.go:21) LEAQ type.map[string]string(SB), AX0x061b 01563 (main.go:21) PCDATA $0, $00x061b 01563 (main.go:21) MOVQ AX, (SP)0x061f 01567 (main.go:21) PCDATA $0, $10x061f 01567 (main.go:21) PCDATA $1, $00x061f 01567 (main.go:21) MOVQ "".m3+64(SP), AX0x0624 01572 (main.go:21) PCDATA $0, $00x0624 01572 (main.go:21) MOVQ AX, 8(SP)0x0629 01577 (main.go:21) PCDATA $0, $10x0629 01577 (main.go:21) LEAQ go.string."1"(SB), AX0x0630 01584 (main.go:21) PCDATA $0, $00x0630 01584 (main.go:21) MOVQ AX, 16(SP)0x0635 01589 (main.go:21) MOVQ $1, 24(SP)0x063e 01598 (main.go:21) CALL runtime.mapaccess2_faststr(SB)0x0643 01603 (main.go:21) PCDATA $0, $10x0643 01603 (main.go:21) MOVQ 32(SP), AX0x0648 01608 (main.go:21) PCDATA $0, $00x0648 01608 (main.go:21) PCDATA $1, $200x0648 01608 (main.go:21) MOVQ AX, ""..autotmp_35+104(SP)0x064d 01613 (main.go:21) MOVBLZX 40(SP), AX0x0652 01618 (main.go:21) MOVB AL, ""..autotmp_13+55(SP)0x0656 01622 (main.go:21) PCDATA $0, $10x0656 01622 (main.go:21) PCDATA $1, $00x0656 01622 (main.go:21) MOVQ ""..autotmp_35+104(SP), AX0x065b 01627 (main.go:21) MOVQ 8(AX), CX0x065f 01631 (main.go:21) MOVQ (AX), AX0x0662 01634 (main.go:21) MOVQ AX, ""..autotmp_12+336(SP)0x066a 01642 (main.go:21) MOVQ CX, ""..autotmp_12+344(SP)0x0672 01650 (main.go:21) PCDATA $0, $00x0672 01650 (main.go:21) PCDATA $1, $210x0672 01650 (main.go:21) MOVQ AX, "".v2+208(SP)0x067a 01658 (main.go:21) MOVQ CX, "".v2+216(SP)0x0682 01666 (main.go:21) MOVBLZX ""..autotmp_13+55(SP), AX0x0687 01671 (main.go:21) MOVB AL, "".ok+54(SP)0x068b 01675 (main.go:21) CMPB "".ok+54(SP), $00x0690 01680 (main.go:21) JNE 16870x0692 01682 (main.go:21) JMP 18620x0697 01687 (main.go:22) MOVQ "".v2+216(SP), AX0x069f 01695 (main.go:22) PCDATA $0, $50x069f 01695 (main.go:22) PCDATA $1, $00x069f 01695 (main.go:22) MOVQ "".v2+208(SP), CX0x06a7 01703 (main.go:22) PCDATA $0, $00x06a7 01703 (main.go:22) MOVQ CX, (SP)0x06ab 01707 (main.go:22) MOVQ AX, 8(SP)0x06b0 01712 (main.go:22) CALL runtime.convTstring(SB)0x06b5 01717 (main.go:22) PCDATA $0, $10x06b5 01717 (main.go:22) MOVQ 16(SP), AX0x06ba 01722 (main.go:22) PCDATA $0, $00x06ba 01722 (main.go:22) PCDATA $1, $220x06ba 01722 (main.go:22) MOVQ AX, ""..autotmp_36+96(SP)0x06bf 01727 (main.go:22) PCDATA $1, $230x06bf 01727 (main.go:22) XORPS X0, X00x06c2 01730 (main.go:22) MOVUPS X0, ""..autotmp_9+272(SP)0x06ca 01738 (main.go:22) PCDATA $0, $10x06ca 01738 (main.go:22) PCDATA $1, $220x06ca 01738 (main.go:22) LEAQ ""..autotmp_9+272(SP), AX0x06d2 01746 (main.go:22) MOVQ AX, ""..autotmp_38+88(SP)0x06d7 01751 (main.go:22) TESTB AL, (AX)0x06d9 01753 (main.go:22) PCDATA $0, $20x06d9 01753 (main.go:22) PCDATA $1, $00x06d9 01753 (main.go:22) MOVQ ""..autotmp_36+96(SP), CX0x06de 01758 (main.go:22) PCDATA $0, $30x06de 01758 (main.go:22) LEAQ type.string(SB), DX0x06e5 01765 (main.go:22) PCDATA $0, $20x06e5 01765 (main.go:22) MOVQ DX, ""..autotmp_9+272(SP)0x06ed 01773 (main.go:22) PCDATA $0, $10x06ed 01773 (main.go:22) MOVQ CX, ""..autotmp_9+280(SP)0x06f5 01781 (main.go:22) TESTB AL, (AX)0x06f7 01783 (main.go:22) JMP 17850x06f9 01785 (main.go:22) MOVQ AX, ""..autotmp_37+352(SP)0x0701 01793 (main.go:22) MOVQ $1, ""..autotmp_37+360(SP)0x070d 01805 (main.go:22) MOVQ $1, ""..autotmp_37+368(SP)0x0719 01817 (main.go:22) PCDATA $0, $00x0719 01817 (main.go:22) MOVQ AX, (SP)0x071d 01821 (main.go:22) MOVQ $1, 8(SP)0x0726 01830 (main.go:22) MOVQ $1, 16(SP)0x072f 01839 (main.go:22) CALL fmt.Println(SB)0x0734 01844 (main.go:22) JMP 18460x0736 01846 (<unknown line number>) PCDATA $0, $-10x0736 01846 (<unknown line number>) PCDATA $1, $-10x0736 01846 (<unknown line number>) MOVQ 640(SP), BP0x073e 01854 (<unknown line number>) ADDQ $648, SP0x0745 01861 (<unknown line number>) RET0x0746 01862 (main.go:21) JMP 18460x0748 01864 (main.go:14) PCDATA $0, $-20x0748 01864 (main.go:14) PCDATA $1, $-20x0748 01864 (main.go:14) LEAQ go.string."2"(SB), AX0x074f 01871 (main.go:14) CALL runtime.gcWriteBarrier(SB)0x0754 01876 (main.go:14) JMP 7680x0759 01881 (main.go:14) NOP0x0759 01881 (main.go:5) PCDATA $1, $-10x0759 01881 (main.go:5) PCDATA $0, $-20x0759 01881 (main.go:5) CALL runtime.morestack_noctxt(SB)0x075e 01886 (main.go:5) PCDATA $0, $-10x075e 01886 (main.go:5) JMP 0
东西有点多,但是我们只分析几个重点 - main.go的第6行、第8行和第12行的map申明分别对应着main.txt23行、57行和132行,都是调用的runtime.makemap_small方法 - main.go的第10行对应着main.txt的97行runtime.makemap方法 - main.go的第14行的赋值对应着main.txt的184行runtime.mapassign_faststr方法 - main.go的第15行的循环对应着main.txt第224行的runtime.mapiterinit方法初始化得到迭代器 - main.go的第19行和第21行分别对着main.txt2个不同的方法runtime.mapaccess1_faststr和runtime.mapaccess2_faststr
runtime/map.go的方法包括上面列举的方法:runtime.makemap_small、runtime.makemap和runtime.mapiterinit方法
由于我的示例代码的map的key是string类型,故runtime.mapassign_faststr、runtime.mapaccess1_faststr和runtime.mapaccess2_faststr方法在runtime/map_faststr.go代码里面,该文件优化了string类型的key的操作:
go的map对应的结构体hmap
go的map的基础结构体是hmap,在runtime/map.go中,
// 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 with the compiler's definition.// map存储的键值对个数count int // # live cells == size of map. Must be first (used by len() builtin)// 表示map的一些标志位flags uint8// map的桶的2的对数就是B值B uint8 // log_2 of # of buckets (can hold up to loadFactor * 2^B items)// 溢出桶个数,是个近似数,不完全相等noverflow uint16 // approximate number of overflow buckets; see incrnoverflow for details// hash种子hash0 uint32 // hash seed// 桶,真正存数据的地方,2^B个桶buckets unsafe.Pointer // array of 2^B Buckets. may be nil if count==0.// 保存一些即将迁移的桶oldbuckets unsafe.Pointer // previous bucket array of half the size, non-nil only when growing// 从oldbuckets迁移到新的buckets的进度nevacuate uintptr // progress counter for evacuation (buckets less than this have been evacuated)// 当key不是指针类型的数据的时候,里面会存溢出桶,这样会避免go的扫描extra *mapextra // optional fields
}
既然是map结构体,为何注释说是一个header呢?这是因为buckets和oldbuckets这2个字段并没有真正存取数据,只是存了一个指针,指向存取buckets的地址,故我们在使用过程中拷贝hmap的时候,并没有真正拷贝map的数据,只是拷贝了hmap这个结构体的一些数据。 在看看hmap的字段mapextra:
// mapextra holds fields that are not present on all maps.
type mapextra struct {// If both key and elem do not contain pointers and are inline, then we mark bucket// 如果key和value都不包括指针并且内敛,然后我们就标记bucket没有指针// type as containing no pointers. This avoids scanning such maps.// 这样会避免gc扫描// However, bmap.overflow is a pointer. In order to keep overflow buckets// 但是,虽然key和value不包括指针,但是逸出桶却是个指针,为了让逸出桶一直// alive, we store pointers to all overflow buckets in hmap.extra.overflow and hmap.extra.oldoverflow.// 存在,所以就把逸出桶和需要迁移的逸出桶的指针存到hamp的extra字段里面// overflow and oldoverflow are only used if key and elem do not contain pointers.// 如果key和elme包含指针,overflow和oldoverflow就不会被使用了// overflow contains overflow buckets for hmap.buckets.// oldoverflow contains overflow buckets for hmap.oldbuckets.// The indirection allows to store a pointer to the slice in hiter.overflow *[]*bmapoldoverflow *[]*bmap// nextOverflow holds a pointer to a free overflow bucket.nextOverflow *bmap
}
如mapextra结构体为啥说有些字段并不会在所有map里面全部都有呢?mapextra是一个为了优化bucket而设计的,当key或value是指针的时候,此时overflow和oldoverflow就不会被使用,只有nextOverflow会被使用,该字段保存了预先申请的逸出桶,在没有发生扩容的时候,而一个桶或者说bmap的8个tophash都被使用完了,那么就要考虑使用逸出桶。 当key和value都没有指针的时候bucket的bmap的_type的ptrdata就是0,意味着该结构体是没有指针的,申请bmap内存的时候,会申请一个没有指针的span,这样会避免gc扫描该内存,会提高效率,但是bmap的最后一个内存块是确确实实存放指针的,所以用uintptr存储着该map的逸出桶的地址,但是由于没有指向下一个逸出桶,可能会被gc回收掉,所以就需要overflow存取指向该逸出桶的指针避免被gc回收掉。 overflow和oldoverflow的用处差不多,只是oldoverflow为了迁移使用,后面的系列会详说。
bucket的结构体bmap
bmap就是真正存数据的结构体了,bmap在源码中定义十分简单:
// A bucket for a Go map.
type bmap struct {// tophash generally contains the top byte of the hash value// for each key in this bucket. If tophash[0] < minTopHash,// tophash[0] is a bucket evacuation state instead.tophash [bucketCnt]uint8// Followed by bucketCnt keys and then bucketCnt elems.// NOTE: packing all the keys together and then all the elems together makes the// code a bit more complicated than alternating key/elem/key/elem/... but it allows// us to eliminate padding which would be needed for, e.g., map[int64]int8.// Followed by an overflow pointer.
}
但其实真正的在内存中分配的结构体是这样的:
// A bucket for a Go map.
type bmap struct {// tophash generally contains the top byte of the hash value// for each key in this bucket. If tophash[0] < minTopHash,// tophash[0] is a bucket evacuation state instead.tophash [bucketCnt]uint8keys [bucketCnt]string // 由于我举例的key是string,故我这里写stringvalues [bucketCnt]string // 由于上面我的例子的value的类型是string,故这里也是stringpointer unsafe.pointer // 由于key和value都是指针,所以这里是一个指针// Followed by bucketCnt keys and then bucketCnt elems.// NOTE: packing all the keys together and then all the elems together makes the// code a bit more complicated than alternating key/elem/key/elem/... but it allows// us to eliminate padding which would be needed for, e.g., map[int64]int8.// Followed by an overflow pointer.
}
隐藏的字段在map源码中都是靠地址偏移来得到,tophash我们能轻易找到位置,但是如何找到keys、values和pointer呢? 在map中是这么做的:
dataOffset = unsafe.Offsetof(struct {b bmapv int64}{}.v)
在64位机器上,需要对齐8个字节,这里int64正好也是8个字节,所以恰好解决内存对齐的问题,找到v的起始地址也就是values的起始地址:
dataOffset+bucketCnt*2*sys.PtrSize // string16个字节,相当于2个指针,bucketCnt等于8
pointer的起始地址
dataOffset+bucketCnt*2*sys.PtrSize+sys.PtrSize
tophash存的是key的hash高8位,为了方便查找key,为什么keys和values分别存在一堆呢?不是k|v|k|v这种呢?由于key和value的变量类型可能不一样,对齐系数不一致,可能导致内存不紧凑而浪费内存,所以把8个keys存到一堆,8个values存到一堆,然后最后在pointer之前对齐就可以了。所以此时也能算出来整个bmap的大小:
bucketCnt*(uint8的字节为1)+bucketCnt*(string的字节16)+bucketCnt*(string的字节16)+指针大小8字节 = 272
在真正调试的时候如查看bucket里面的具体的key和value呢?这里给大家展示一个小技巧,如果大家有其他方法,可以留言讨论:
type dmap struct {tophash [bucketCnt]uint8debugKeys [bucketCnt]stringdebugElems [bucketCnt]string//debugOverflows unsafe.PointerdebugOverflows uintptr
}
这是我定义的调试结构体,将该代码和bmap的结构体放到一堆,当你获取到bmap桶的地址的时候,就可以如下转换,就可以查看bmap的具体值了:
b0 := (*dmap)(add(buckets, uintptr(0)*uintptr(t.bucketsize)))println(b0.debugOverflows)
buckets假设是桶数组的起始地址,加上bucketsize就会得到第二个桶的起始地址,然后直接转型为*dmap,最后你可以可以打印出来你想查看的真实的值了。
map的整体结构
如果申请内存桶的时候又多余的溢出桶,那么mapextra的nextOverflow就会指向[]bmap其中的某块桶的地址,地址后之后的桶都是溢出桶。在一个桶装不下的时候,会去溢出桶拿桶然后bmap的overflow指向溢出桶。
总结
上面大概介绍了map的数据结构,后面系列map的具体代码分析,包括增、删、改、查、扩容等都会分篇细解,如有不足之处,请共同讨论学习。