本文转载自微信公众号《码农桃花源》,作者小X,转载本文请联系码农桃花源公众号。大家好,我是小X,曹达最近开了围棋课程,小X跟曹达一起围棋。本系列将讨论从课程中学到的一些有启发性的东西,拨开乌云,带你回到Go。熟悉map结构的读者应该知道,hmap是由很多bmap(桶)组成的,每个bmap存储8个key/value对:hmap有时同一个bmap中的key/value太多,超过8个,会被overflowbmap接管,也就是overflowbmap(后面我们称它为bucket)。溢出的桶与原来的桶形成一个“拉链”。对于这些溢出桶,在hmap结构和bmap结构中分别有extra.overflow和overflow字段指向它们。如果我们仔细看mapextra结构体中溢出字段的注释,就会发现这里有“文章”。typemapextrastruct{overflow*[]*bmapoldoverflow*[]*bmapnextOverflow*bmap}overflow字段有一大段注释,我们先看前两行://Ifbothkeyandelemdonotcontainpointersandareinline,thenwemarkbucket//typeascontainingnopointers.Thisavoidsscanningsuchmaps.意思是如果映射键和值都不包含指针,则避免在GC期间扫描它。在地图非常大(百万个key)的场景下,可以提升很多性能。如何实现“免扫描”?我们知道bmap结构体中有一个溢出指针,指向溢出桶。因为是指针,所以GC的时候一定要扫描,所有的bmap都要扫描。并且当map的key/value为非指针类型时,可以避免扫描,直接标记整个map的颜色即可(三色标记法),而不用扫描每个bmap的溢出指针.但是无论键/值的类型如何,溢出桶总是可能的。于是利用hmap中extra结构的溢出指针来“容纳”这些溢出桶,将bmap结构的溢出指针类型改为unitptr类型(这些都是在编译时完成的)。所以整个bmap根本就没有指针,GC的时候也不会扫描到。overflow*[]*bmap另一方面,GC在扫描hmap时,extra.overflow的路径(指针)可以正常将溢出桶标记为黑色,这样就不会被GC误回收。当我们知道了上面的原理后,我们就可以利用它来优化一些场景的性能了:map[string]int->map[[12]byte]int因为字符串的底部有指针,当字符串被使用时作为map的key,GC阶段会扫描整个map;数组[12]byte是值类型,不会被GC扫描。我们使用两种方法来验证优化效果。主动触发GC这里的测试代码来自文章《尽量不要在大 map 中保存指针》[1]:funcMapWithPointer(){constN=10000000m:=make(map[string]string)fori:=0;i
