Golang 中的字符串内存使用
String memory usage in Golang
我正在使用 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 个字节,与 a 一样只有一个信件 ?
unsafe.Sizeof()
不会递归地进入数据结构,它只是报告传递值的“浅”大小。引用其文档:
The size does not include any memory possibly referenced by x. For instance, if x is a slice, Sizeof returns the size of the slice descriptor, not the size of the memory referenced by the slice.
Go 中的映射是作为指针实现的,因此 unsafe.Sizeof(somemap)
将报告该指针的大小。
Go 中的字符串只是 headers 包含一个指针和一个长度。见 reflect.StringHeader
:
type StringHeader struct {
Data uintptr
Len int
}
因此unsafe.Sizeof(somestring)
将报告上述结构的大小,它与string
值(即Len
字段的值)的长度无关。
要获取地图(“深度”)的实际内存需求,请参阅How much memory do golang maps reserve? and also How to get memory size of variable in Go?
Go 将 string
值的 UTF-8 编码字节序列存储在内存中。内置函数 len()
报告 string
的 byte-length,所以
基本上在内存中存储 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
的整个后备数组仍将保留。
我正在使用 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 个字节,与 a 一样只有一个信件 ?
unsafe.Sizeof()
不会递归地进入数据结构,它只是报告传递值的“浅”大小。引用其文档:
The size does not include any memory possibly referenced by x. For instance, if x is a slice, Sizeof returns the size of the slice descriptor, not the size of the memory referenced by the slice.
Go 中的映射是作为指针实现的,因此 unsafe.Sizeof(somemap)
将报告该指针的大小。
Go 中的字符串只是 headers 包含一个指针和一个长度。见 reflect.StringHeader
:
type StringHeader struct {
Data uintptr
Len int
}
因此unsafe.Sizeof(somestring)
将报告上述结构的大小,它与string
值(即Len
字段的值)的长度无关。
要获取地图(“深度”)的实际内存需求,请参阅How much memory do golang maps reserve? and also How to get memory size of variable in Go?
Go 将 string
值的 UTF-8 编码字节序列存储在内存中。内置函数 len()
报告 string
的 byte-length,所以
基本上在内存中存储 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
的整个后备数组仍将保留。