【问题标题】:String memory usage in GolangGolang 中的字符串内存使用情况
【发布时间】:2019-03-21 22:48:47
【问题描述】:

我正在使用 ma​​p[string]string 优化代码,其中地图的值只有“A”或“B”。所以我认为显然 ma​​p[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 只有一个字母?

【问题讨论】:

  • 使用 go 的 profiler 优化程序。
  • 通用且有用的规则:始终仔细阅读每个函数和每个包的官方文档。答案就在那里。

标签: string go memory size


【解决方案1】:

unsafe.Sizeof() 不会递归地进入数据结构,它只是报告传递值的“浅”大小。引用其文档:

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

Go 中的地图是作为指针实现的,因此unsafe.Sizeof(somemap) 将报告该指针的大小。

Go 中的字符串只是包含指针和长度的标头。见reflect.StringHeader:

type StringHeader struct {
        Data uintptr
        Len  int
}

所以unsafe.Sizeof(somestring)会报告上述结构的大小,它与string值的长度(即Len字段的值)无关。

要获取映射的实际内存需求(“深度”),请参阅 How much memory do golang maps reserve?How to get memory size of variable in 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 的整个后备数组。

【讨论】:

  • 感谢您的回复!所以基本上遵循 StringHeader 结构,内存中字符串的大小等于 sizeof(uintptr) * Len ?
  • @Dylan 不,StringHeader.Data 是一个指针,字符串的字节(字符)存储在内存中。 string 值的大小基本上是它的长度(它是一个字节长度)加上标头的大小。请参阅编辑后的答案。
  • 尝试运行示例时出现错误play.golang.org/p/ugZoV5PeE9H
  • @sprut 感谢您的注意,我已修复它(它只需要 int 转换)。
  • @sprut 这里是你的例子更正了play.golang.org/p/PyX257Efll1
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-07-11
  • 2011-03-02
相关资源
最近更新 更多