Golang中的字符串内存使用情况

20jt8wwn  于 2022-12-25  发布在  Go
关注(0)|答案(1)|浏览(178)

我使用map [string] string优化代码,其中map的值只有"A"或"B",所以我想显然map [string] bool要好得多,因为map包含大约5千万个元素。

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个字节的一个很长的字符串,与a一样,只有一个字母?

tzdcorbm

tzdcorbm1#

unsafe.Sizeof()不会递归地进入数据结构,它只报告传递值的"浅"大小。

    • 该大小不包括x可能引用的任何内存。**例如,如果x是一个切片,则Sizeof返回切片描述符的大小,而不是切片引用的内存大小。

Go语言中的Map是以指针的形式实现的,所以unsafe.Sizeof(somemap)会报告指针的大小。
Go语言中的字符串只是包含一个指针和一个长度的头部,参见reflect.StringHeader

type StringHeader struct {
        Data uintptr
        Len  int
}

因此unsafe.Sizeof(somestring)将报告上述结构体的大小,该大小与string值(Len字段的值)的长度无关。
要获得Map的实际内存需求("深度"),请参阅golangMap保留多少内存?以及如何获得Go语言中变量的内存大小?
Go语言将string值的UTF-8编码的字节序列存储在内存中,内置函数len()报告string的字节长度,因此基本上存储string值所需的内存为:

var str string = "some string"

stringSize := len(str) + int(unsafe.Sizeof(str))

另外,不要忘记,string值可以通过对另一个更大的字符串进行切片来构造,因此,即使原始字符串不再被引用(因此不再需要),仍然需要将更大的支持数组保存在内存中,以用于较小的字符串切片。
例如:

s := "some loooooooong string"
s2 := s[:2]

这里,即使s2的内存需求是len(s2) + unsafe.Sizeof(str) = 2 + unsafe.Sizeof(str)s的整个后备数组仍将保留。

相关问题