【问题标题】:How to minimize the garbage collection in Go?如何最小化 Go 中的垃圾收集?
【发布时间】:2012-09-05 08:33:19
【问题描述】:

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

我认为下一个是正确的:

  • 在函数开头声明变量。
  • 使用数组代替切片。

还有吗?

【问题讨论】:

  • 我猜你应该在做任何这样的优化之前先分析一下。查看 this 关于分析的博客条目。他们还优化了垃圾收集。应该是一个好的开始吗?
  • 为什么关闭?问题:如何避免垃圾收集?避免堆分配(带有特定的参考示例)。这个问题是相关的和具体的,答案似乎没有那么值得商榷。

标签: garbage-collection go


【解决方案1】:

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

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

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

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

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

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

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

【讨论】:

  • 可以使用go build -gcflags="-m"查看编译器的逃逸分析。在可能的情况下将x := make([]byte, 42) 之类的东西放在堆栈上会变得更好。
【解决方案2】:

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

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

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

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

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

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

【讨论】:

    猜你喜欢
    • 2017-04-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-04-10
    • 2013-05-30
    • 2011-12-11
    • 2011-01-21
    相关资源
    最近更新 更多