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