我正在使用 map [string] string 优化代码,其中 map 的值仅为“ A”或“ B”。因此,我认为显然, map [string] bool 更好,因为该地图可容纳约5000万个元素。
var a = "a" var a2 = "Why This ultra long string take the same amount of space in memory as 'a'" var b = true var c map[string]string var d map[string]bool c["t"] = "A" d["t"] = true fmt.Printf("a: %T, %d\n", a, unsafe.Sizeof(a)) fmt.Printf("a2: %T, %d\n", a2, unsafe.Sizeof(a2)) fmt.Printf("b: %T, %d\n", b, unsafe.Sizeof(b)) fmt.Printf("c: %T, %d\n", c, unsafe.Sizeof(c)) fmt.Printf("d: %T, %d\n", d, unsafe.Sizeof(d)) fmt.Printf("c: %T, %d\n", c, unsafe.Sizeof(c["t"])) fmt.Printf("d: %T, %d\n", d, unsafe.Sizeof(d["t"]))
结果是:
a: string, 8 a2: string, 8 b: bool, 1 c: map[string]string, 4 d: map[string]bool, 4 c2: map[string]string, 8 d2: map[string]bool, 1
在测试时,我发现有些奇怪,为什么带有很长字符串的 a2 使用8个字节,就像 一个 只有一个字母一样?
unsafe.Sizeof()不会递归地进入数据结构,它只是报告传递的值的“浅”大小。引用其文档:
unsafe.Sizeof()
该大小不包括x可能引用的任何内存。 例如,如果x是切片,则Sizeof返回切片描述符的大小,而不是该切片所引用的内存的大小。
Go中的地图被实现为指针,因此unsafe.Sizeof(somemap)将报告该指针的大小。
unsafe.Sizeof(somemap)
Go中的字符串只是包含指针和长度的标头。见reflect.StringHeader:
reflect.StringHeader
type StringHeader struct { Data uintptr Len int }
因此unsafe.Sizeof(somestring)将报告上述结构的大小,该大小与string值的长度(Len字段的值)无关。
unsafe.Sizeof(somestring)
string
Len
要获得映射的实际内存需求(“深度”),Go将UTF-8编码的string值的字节序列存储在内存中。内置函数len()报告a的字节长度string,因此基本上在内存中存储string值所需的内存为:
len()
var str string = "some string" stringSize := len(str) + unsafe.Sizeof(str)
同样不要忘记,string可以通过切片另一个更大的字符串来构造一个值,因此,即使不再引用原始字符串(因此不再需要),也仍然需要将更大的支持数组保留在内存中用于较小的字符串切片。
例如:
s := "some loooooooong string" s2 := s[:2]
在这里,即使内存要求的s2是len(s2) + unsafe.Sizeof(str) = 2 +unsafe.Sizeof(str),尽管如此,整个支持数组s将被保留。
s2
len(s2) + unsafe.Sizeof(str) = 2 +unsafe.Sizeof(str)
s