【问题标题】:Range loop on a Go channel and garbage collection of the loop variableGo 通道上的范围循环和循环变量的垃圾收集
【发布时间】:2021-02-19 22:20:13
【问题描述】:

考虑以下 Go 代码:

type LargeStructWithNestings struct {...}
func generatorChnl() <-chan *LargeStructWithNestings { ... }

// code snippet
chnl := generatorChnl()
for entry := range chnl {    // line A
    doStuffWith(entry)
    entry = nil              // line B
}

假设通道不经常产生值,并且这些值是指向大型结构的指针,那么上面的line B 是否会导致entry 指向的内存的垃圾收集比其他情况更早发生?或者,一旦循环回到line Aentry 指向的内存中的前一个位置是否会在下一次迭代阻塞line A 时立即进行垃圾收集?

我们的目标是找到一种方法来尽可能地减少驻留集的大小。

【问题讨论】:

  • 指向entry的内存在什么时候有资格进行垃圾回收?只有在下一个条目产生/通道关闭时?还是在产生它的迭代结束时立即?
  • 内存在无法访问的情况下可以被回收。这更取决于 doStuffWith 在您的示例中所做的事情。试图以这种方式控制 GC 通常会让你一事无成。如果你想减少内存,你需要分配更少的内存。您也可以尝试使用较低的 GOGC 设置来提高攻击性,但这也会增加 CPU 使用率和花费在 GC 上的总时间。
  • @JimB 这里的问题是在下一次迭代中:如果一个值已经存储在entry中,并且接收另一个元素长时间阻塞,之前的元素将保留在entry中直到接收可以继续。
  • @icza:我明白你的意思,但是相同的数量会在下一次迭代中立即重新分配,并且运行时不太可能释放无论如何都会被重用的内存。
  • @CppNoob:还记得垃圾收集不会释放内存,垃圾收集器的工作是重用内存。稍早收集值并不意味着内存可以释放给操作系统。

标签: go memory-management memory-leaks garbage-collection


【解决方案1】:

for 语句不会清除迭代之间的循环变量。有一个循环变量在每次迭代中重复使用。因此,在从通道接收到新值之前,它将保存最后分配给它的值。

所以是的,如果从chnl 接收“长时间”阻塞,循环变量entry 将保留对最后接收(和分配)项目的“引用”,从而防止它被垃圾收集。

在实践中,这很少——如果有的话——是一个问题。来自通道的值可能“经常”接收,接收的值可能“小”,也可能有其他引用,以及这些的任意组合。

如果在您的特定情况下这确实是一个问题,您可以清除该变量,但我从未遇到任何需要它的示例。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2019-02-22
    • 1970-01-01
    • 2016-05-31
    • 2012-12-28
    • 1970-01-01
    • 2013-05-11
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多