你好,
我花了很多时间去寻找一个并不存在的“内存泄漏”的原因。这是因为我们对内存管理存在根本性的误解。我们认为垃圾回收器会将未使用的堆内存返回给操作系统,这是错误的。
解释在#41444(评论)中:
"由于Go垃圾回收器的设计是保留大约两倍于活动堆大小的内存,因此debug.FreeOSMemory预计可以释放大约一半的内存"
你能否将这些信息添加到https://tip.golang.org/doc/gc-guide中?
我认为了解这一点非常重要。
你好,
我花了很多时间去寻找一个并不存在的“内存泄漏”的原因。这是因为我们对内存管理存在根本性的误解。我们认为垃圾回收器会将未使用的堆内存返回给操作系统,这是错误的。
解释在#41444(评论)中:
"由于Go垃圾回收器的设计是保留大约两倍于活动堆大小的内存,因此debug.FreeOSMemory预计可以释放大约一半的内存"
你能否将这些信息添加到https://tip.golang.org/doc/gc-guide中?
我认为了解这一点非常重要。
5条答案
按热度按时间hsgswve41#
这部分内容原本是打算在 https://tip.golang.org/doc/gc-guide#GOGC 中解释的,尽管那一部分文字较多,可能可以更直接、简洁地提及总堆大小。
66bbxpm52#
我们认为垃圾回收器将未使用的堆内存返回给操作系统,这是错误的。
澄清一下,它确实将未使用的堆内存返回给操作系统,但它不会将内存返回给它认为在不久的将来会使用的操作系统。它通常会保留足够的内存供应用程序分配到下一个堆目标(
runtime.MemStats.NextGC
,或runtime/metrics
度量/gc/heap/goal:bytes
),而无需操作系统分页进入新的物理内存,这很昂贵。我认为(但不确定)这就是你看到的。
"由于Go垃圾回收器的设计是保留大约两倍于活动堆大小的内存,因此debug.FreeOSMemory预计可以释放大约一半的内存"
这句话是正确的,但其中有很多细微之处。如果你的应用程序定期分配内存,
debug.FreeOSMemory
可能会看起来几乎没有效果,因为结果将是短暂的。正如奥斯汀在您链接的评论中指出的那样,活跃的应用程序很快就会再次使用这些内存。当你期望应用程序长时间处于空闲状态时,这种情况就更有意义了。同时,调用debug.FreeOSMemory
可能会产生大量的CPU成本。如果你的应用程序处于活动状态,那么刚刚释放给操作系统的内存很可能会被立即分页,正如我上面提到的,这是昂贵的。它还强制执行完整的GC周期,增加GC频率。debug.FreeOSMemory
应该放在GC指南中,但我尽量避免推荐debug.FreeOSMemory
,因为它真的是最后的手段。这个解释应该在https://tip.golang.org/doc/gc-guide#GOGC中解释,尽管该部分有更多的文字和可能更直接、简洁地提及总堆大小。
顺便说一下,总堆大小在一个句子中定义,位于该部分的顶部。重新阅读该部分,大部分文本似乎都是脚注和示例(即“可以用来关闭GC”,“Go 1.18做了......”,“这里有一个例子”,“这里有如何使用交互式示例”)。将所有这些放在一起确实会让一个人的眼睛发呆。我承认我们可以通过将脚注移动到实际的脚注中并将示例放在“示例”子标题下使这个过程变得更好。我认为如果在第几句话后停止阅读定义本身是可以接受的。
这并没有真正解决“眼睛发呆”的问题,但我认为这里可能缺少的是一句像“GC始终为应用程序分配足够的内存以达到目标堆大小”这样的简短句子。也许如果我们重新组织该部分,额外的句子就不会那么糟糕了。
lbsnaicq3#
我的应用程序按需分配内存,例如在收到Web请求时。令人烦恼的是,它不会将所有已使用的内存返回给操作系统,无论我们等待多长时间。因此,从操作系统的Angular 来看,内存使用量没有恢复到我们在发出Web请求之前的水平。
对我来说,返回的确切数量并不重要,但事实是它没有返回所有空闲内存。老实说,我对gc-guide的理解只是部分的。也许这就是为什么从操作系统的Angular 来看,最终结果对我来说并不明确的原因。
无论如何,我已经确保内存使用量不会随着时间的推移而不断增加。这运行得很好。
q5iwbnjs4#
令人烦恼的是,它不会将所有已使用的内存返回给操作系统,无论我们等待多长时间。出于好奇,你等了多久?如果没有更早地运行,每2分钟就会强制进行一次垃圾回收。一旦发生这种情况,后台清理程序应该逐渐将内存返回给操作系统。
zyfwsgd65#
我等了两天。
也许附上的图表有所帮助。
我认为图表显示的内容与top命令中的RES列相同。
昨天部署了两个示例的模块,它们最初消耗的内存量相同。在经过一些负载后,绿色的pod始终比黄色的pod使用更多的内存,即使在一整晚几乎没有活动的情况下也是如此。
今天早上我在07:35-07:38之间产生了一些负载。您可以看到两个pod的内存使用率都没有恢复到原始水平。这就是我们最初解释为内存泄漏的原因。
我已经90%确定我们没有真正的内存泄漏,因为几天前我为两个小时的时间创建了大量的负载,其中内存使用量很高,但并没有持续增加。我的老板有95%的把握 😃
绿色pod在08:45收集的度量指标(不确定它们是否有帮助):