什么变量的大小是 0 字节
查看一个变量的字节大小
go
代码解读
复制代码
fmt.Println(unsafe.Sizeof(int(0))) // 8
int
类型的变量大小是8
字节,int
类型的变量大小是不固定的,会因为不同的操作系统而改变int32
类型的变量大小是4
字节int64
类型的变量大小是8
字节- 指针的字节长度是
8
字节,也会根据操作系统的不同而改变
空结构体的大小是 0
字节
go
代码解读
复制代码
type Person struct {} func main() { p := Person{} fmt.Println(unsafe.Sizeof(p)) // 0 }
0
字节的变量在内存中的地址是相同的,称为 zerobase
,这个变量在 runtime/malloc.go
文件中
go
代码解读
复制代码
fmt.Printf("%p\n", &Person{}) // 0x1164340 -> 是 zerobase
不过要注意的是,空结构体如果在其他结构体(这个结构体中的成员要有非空结构体)中,那么这个结构体的地址就不是 zerobase
了
go
代码解读
复制代码
type Person1 struct { p1 Person } p1 := Person1{} fmt.Printf("%p\n", &p1.p1) // 0x1164340 -> 是 zerobase
go
代码解读
复制代码
type Person1 struct { p1 Person name string } p1 := Person1{} fmt.Printf("%p\n", &p1.p1) // 0xc000014070 -> 不是 zerobase
空结构体的主要作用就是为了节省内存,使用场景:hashset
、channel
字符串的底层
字符串在 go
中占用的是 16
字节,不管字符串的长度是多少,都是 16
字节
go
代码解读
复制代码
fmt.Println(unsafe.Sizeof("a")) // 16 fmt.Println(unsafe.Sizeof("高并发下的数据结构")) // 16
字符串的底层是一个结构体 stringStruct
,结构体中有两个字段,一个是指向字符串的指针,一个是字符串的长度
这个结构体在 runtime/string.go
文件中
go
代码解读
复制代码
type stringStruct struct { str unsafe.Pointer len int }
但是这个结构体不能被外部直接访问
在反射包里面有一个 StringHeader
结构体,和 stringStruct
结构体是类似
这个结构体在 reflect/value.go
文件中
go
代码解读
复制代码
type StringHeader struct { Data uintptr Len int }
StringHeader
中的 Len
字段是字符串的字节长度,这个长度和 len
方法返回的长度是一样的
go
代码解读
复制代码
s := "高并发" fmt.Println((*reflect.StringHeader)(unsafe.Pointer(&s)).Len) // 9 len(s) // 9
字符串遍历应该使用 range
关键字,因为 range
关键字会自动处理 utf-8
编码的字符串
go
代码解读
复制代码
s := "高并发" for _, char := range s { fmt.Println(string(char)) }
如果要切割字符串的话需要将字符串转成 rune
类型的切片
go
代码解读
复制代码
s := "高并发" runeSlice := []rune(s) fmt.Println(string(runeSlice[0])) // 高
map 的底层
map
的底层是 hmap
,在 runtime.map.go
文件中,结构如下:
go
代码解读
复制代码
// A header for a Go map. type hmap struct { // Note: the format of the hmap is also encoded in cmd/compile/internal/reflectdata/reflect.go. // Make sure this stays in sync with the compiler's definition. count int // # live cells == size of map. Must be first (used by len() builtin) flags uint8 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 hash0 uint32 // hash seed 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 nevacuate uintptr // progress counter for evacuation (buckets less than this have been evacuated) extra *mapextra // optional fields }
buckets
:有这个属性的就是拉链法B
:buckets
数量的2
的对数count
:当前这个hamp
中存储的key-value
的数量hash0
:哈希算法的种子extra
:溢出桶的信息
bucket
的结构如下:
go
代码解读
复制代码
// 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. }
tophash
:存储的是每一个key
的前一个字节的哈希值overflow
:溢出指针
go
代码解读
复制代码
func makemap(t *maptype, hint int, h *hmap) *hmap { mem, overflow := math.MulUintptr(uintptr(hint), t.Bucket.Size_) if overflow || mem > maxAlloc { hint = 0 } // initialize Hmap if h == nil { // new 了一个 hmap 结构体 h = new(hmap) } h.hash0 = fastrand() // Find the size parameter B which will hold the requested # of elements. // For hint < 0 overLoadFactor returns false since hint < bucketCnt. B := uint8(0) // 算出 B 的值 for overLoadFactor(hint, B) { B++ } h.B = B // allocate initial hash table // if B == 0, the buckets field is allocated lazily later (in mapassign) // If hint is large zeroing this memory could take a while. if h.B != 0 { var nextOverflow *bmap // 创建一些 bucket 和 overflow bucket h.buckets, nextOverflow = makeBucketArray(t, h.B, nil) if nextOverflow != nil { h.extra = new(mapextra) // overflow bucket 保存在 extra 中 h.extra.nextOverflow = nextOverflow } } return h }
map
初始结构:
hash
值通过hash0
和key
计算出来tophash
取hash
值的高8
位- 放第几个
bucket
,通过B
来判断,B = 3
表示取hash
值的低3
位,然后把这3
位换算成十进制