golang 空struct内存占用为0,且所有的空struct都引用的是同一地址
func TestEmptyStruct(t *testing.T) {
type emptyStruct struct{}
type emptyInside struct {
emptyStruct
}
type personStruct struct {
E1 emptyStruct
A struct {
Age int
}
E2 emptyStruct
S struct {
Sex string
}
E3 struct {
}
}
a := struct{}{}
b := emptyStruct{}
eInside := emptyInside{emptyStruct: emptyStruct{}}
fmt.Printf("%p\n", &a)
fmt.Printf("%p\n", &b)
fmt.Printf("%p\n", &eInside)
fmt.Println("size of a is ", unsafe.Sizeof(a), " size of b is ", unsafe.Sizeof(b), " size of eInside is ", unsafe.Sizeof(eInside))
// 0x2c2bb60
// 0x2c2bb60
// 0x2c2bb60 -> 空结构体(包括不断嵌套但仍然无任何内置字段的空结构体)的变量的内存地址都是一样的,所有空struct都是引用全局变量zerobase的地址
// size of a is 0 size of b is 0 size of eInside is 0
c := personStruct{}
fmt.Printf("%s, %p\n", "c", &c)
fmt.Printf("%s, %p\n", "c.E1", &c.E1)
fmt.Printf("%s, %p\n", "c.A", &c.A)
fmt.Printf("%s, %p\n", "c.E2", &c.E2)
fmt.Printf("%s, %p\n", "c.S", &c.S)
fmt.Printf("%s, %p\n", "c.E3", &c.E3)
// c, 0xc00016b8e0
// c.E1, 0xc00016b8e0
// c.A, 0xc00016b8e0 -> c, c.E1, c.A地址相同,空结构体不占任何内存空间
// c.E2, 0xc00016b8e8 -> e8 - e0 = 8. golang int类型在32位系统4字节 64位系统8字节
// c.S, 0xc00016b8e8
// c.E3, 0xc00016b8f8 -> f8 - e8 = 16
// golang string数据类型占用16字节空间,
// 前8字节是一个指针,指向字符串值的地址,
// 后八个字节是一个整数,标识字符串的长度;
// 注意go语言的字符串内部并不以'\0'作为结尾,而是通过一个长度域来表示字符串的长度
}
go编译器在编译期间,识别到 struct {} 这种特殊类型的内存分配,会统统分配出 runtime.zerobase 的地址出去,代码逻辑是在 mallocgc 函数里面:
代码如下:
func mallocgc(size uintptr, typ *_type, needzero bool) unsafe.Pointer {
// 分配 size 为 0 的结构体,把全局变量 zerobase 的地址给出去即可;
if size == 0 {
return unsafe.Pointer(&zerobase)
}
// ...
网友评论