什么变量的大小是 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
位换算成十进制