如何减少[]字符串的内存使用量?

时间:2016-04-26 09:33:15

标签: go

对我来说,Go是一个新手,我在理解内存使用方面遇到了一些麻烦:

我想将类似于csv的文件加载到一个行数组中,每一行都是一个结构,由一个22字符的键和一个值数组(字符串)组成。
我的代码如下所示:https://play.golang.org/p/hJ4SHjVXaG

问题是对于450M的文件,它使用大约2G1的内存 有没有人有减少内存使用的解决方案?

使用SirDarius解决方案进行更新:https://play.golang.org/p/DBmOFOkZdx仍然使用大约1G9

4 个答案:

答案 0 :(得分:6)

文件中有多少行和字段?

你所描述的是使用最少量的内存是合理的。

查看代码,我认为它将为底层字符串数据使用450MB内存。

然后它会将其分成字符串。它们由指针和长度组成,在64位平台上占用16个字节。

所以1.5GB / 16 = 93Million。

因此,如果您的文件中有大约50万个字段,那么内存使用似乎是合理的。

还有其他开销,例如行数等,所以这不是一个精确的计算。

修改

鉴于
每行5百万行,每列10列

这是50万个16字节的字符串标题,需要800MB。加上数据本身450MB,再加上5 * 8 * 500万行= 200MB,使得1.45GB

所以即使有完美的内存分配,我也不会想到,你能够将使用量降低到1.5GB以下。

答案 1 :(得分:2)

这对我来说似乎效率很低:

for _, value := range strings.Split(line[23:], ";") {
    row.Values = append(row.Values, value)
}

您基本上通过调用[]string函数获取string.Split,然后遍历该片段以将每个字符串附加到另一个最初的nil字符串片段。

为什么不这样做:

row.Values = strings.Split(line[23:], ";")

相反?

虽然我无法保证,但循环可能会导致每个字符串被复制,因此会使程序使用两次作为内存。

答案 2 :(得分:1)

您正在将每次迭代获得的值附加到Row结构中,考虑到巨大的文件大小并不是一种合理的好方法。为什么你不批量处理文件?

查看Split函数会返回一个子字符串片段,因此无需对结果片段进行范围调整并将其附加到row.Values。您可以将结果值直接分配给row.Values,然后将其附加到rows切片。

func Split(s, sep string) []string
  

将切片分割成由sep分隔的所有子串并返回a   这些分隔符之间的子串的切片。如果sep为空,   在每个UTF-8序列之后拆分拆分。它相当于SplitN   计数为-1。

row.Values = strings.Split(line[23:], ";")
rows = append(rows, row)

答案 3 :(得分:0)

对我来说这是关于append()函数的。来自语言规范

  

如果s的容量不足以容纳附加值,   append分配一个新的,足够大的底层数组

这个新分配的数组的大小足以消耗更多的附加内容。因此,要准确分配,您应该slice := make([]Row, 0, WithExpectedCapacity)而不是slice[n]=而不是append()。如果你不能这样做,你至少可以尝试反射来压缩

reflect.ValueOf(&slice).Elem().SetCap(len(slice))

有些棘手,但你可以看到https://play.golang.org/p/LslkOBCvII它有效。