25

有时您可能希望避免/最小化垃圾收集器,所以我想确定如何去做。

我认为下一个是正确的:

  • 在函数的开头声明变量。
  • 使用数组而不是切片。

还有吗?

4

2 回答 2

37

要最小化 Go 中的垃圾收集,您必须最小化堆分配。要最小化堆分配,您必须了解分配发生的时间。

以下事情总是会导致分配(至少在 Go 1 的 gc 编译器中):

  • 使用new内置函数
  • 使用make内置函数(除了一些不太可能的极端情况)
  • 当值类型是切片、映射或带有&运算符的结构时的复合文字
  • 将大于机器字的值放入接口。(例如,字符串、切片和某些结构比机器字大。)
  • string在, []byte, 和之间转换[]rune
    • m[string(b)]从 Go 1.3 开始,编译器在特殊情况下m不分配此表达式b[]byte
  • 将非常量整数值转换为string
  • defer陈述
  • go陈述
  • 捕获局部变量的函数字面量

以下情况可能会导致分配,具体取决于详细信息:

  • 获取变量的地址。请注意,可以隐式获取地址。例如,可能会获取if is not a pointera.b()的地址,并且该方法具有指针接收器类型。aab
  • 使用append内置函数
  • 调用可变参数函数或方法
  • 切片数组
  • 向地图添加元素

该列表旨在完整,我对此充满信心,但很高兴考虑添加或更正。

如果您不确定分配发生在哪里,您始终可以按照其他人的建议进行概要分析,或者查看编译器生成的程序集。

于 2012-09-05T21:38:10.047 回答
27

避免垃圾是相对简单的。您需要了解分配的位置,看看是否可以避免分配。

首先,在函数开头声明变量不会有帮助。编译器不知道区别。但是,人类会知道其中的区别,并且会惹恼他们。

使用数组而不是切片将起作用,但那是因为数组(除非取消引用)被放入堆栈。数组还有其他问题,例如它们在函数之间按值传递(复制)。堆栈上的任何东西都“不是垃圾”,因为它会在函数返回时被释放。任何可能逃脱函数的指针或切片都放在垃圾收集器必须在某个时候处理的堆上。

你能做的最好的事情就是避免分配。当您处理完不需要的大量数据时,请重新使用它们。这是Go 博客上的分析教程中使用的方法。我建议阅读它。

除了分析教程中的示例之外的另一个示例:假设您有一个[]int名为xs. 你不断地追加到,[]int直到你达到一个条件,然后你重置它,这样你就可以重新开始。如果你这样做xs = nil,你现在将切片的底层数组声明为要收集的垃圾。然后,Append 将在您下次使用时重新分配 xs。相反,如果您这样做xs = xs[:0],您仍在重置它但保留旧数组。

在大多数情况下,试图避免产生垃圾是过早的优化。对于您的大多数代码来说,这并不重要。但是你可能偶尔会发现一个函数被调用很多次,每次运行时都会分配很多。或者你重新分配而不是重用的循环。我会等到你看到瓶颈后再过火。

于 2012-09-05T19:18:07.423 回答