Golang中不同版本下scavenger的行为差异解析

Golang中不同版本下scavenger的行为差异解析 我观察到,在不同Go版本中,RSS(常驻集大小)释放给操作系统的行为有所不同。例如,在Go 1.16下,RSS在5小时后释放,而在Go 1.18下则释放得非常快。我在Go 1.18 发布说明 - Go编程语言中找到了这样的描述:“运行时现在能更高效地将内存返还给操作系统,并且经过调整后,其行为变得更加积极。”但我不知道这具体指的是哪项改进?我在GitHub上看到了许多关于清理器(scavenger)的问题和讨论。是否有关于清理器发展历史的解释?在每个Go版本中,我们应该期待什么样的行为?

另外,基于这个讨论 https://github.com/golang/go/issues/36398#issuecomment-648753457,即使设置了 GODEBUG=madvdontneed=1,如果像 memory.soft_limit_in_bytes 这样的cgroup配置不同,我是否应该预期RSS的释放延迟也会不同?


更多关于Golang中不同版本下scavenger的行为差异解析的实战教程也可以访问 https://www.itying.com/category-94-b0.html

1 回复

更多关于Golang中不同版本下scavenger的行为差异解析的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


在 Go 1.18 中,scavenger 的行为确实发生了显著变化,这主要归功于对内存返还策略的优化。具体来说,Go 1.18 引入了更积极的 scavenger 策略,通过改进内存页的释放算法和时机,减少了 RSS 的保留时间。关键改进包括:

  1. 动态调整 scavenger 的触发阈值:Go 1.18 优化了内存压力检测机制,使得 scavenger 在堆内存下降时更快启动。
  2. 减少延迟:通过更频繁地扫描和释放未使用的内存页,RSS 释放的延迟从 Go 1.16 的约 5 小时缩短到几分钟甚至更短。

关于 scavenger 的发展历史,主要版本变化如下:

  • Go 1.12 之前:scavenger 行为较为保守,内存返还较慢。
  • Go 1.12:引入了更主动的 scavenger,但仍有延迟。
  • Go 1.16:优化了 scavenger 的触发条件,但释放延迟仍较高(如您观察到的 5 小时)。
  • Go 1.18:大幅改进,采用更积极的策略,显著降低延迟。

示例代码中,虽然无法直接控制 scavenger,但可通过 runtime/debug 设置内存限制来间接影响其行为:

package main

import (
    "runtime/debug"
    "time"
)

func main() {
    // 设置内存软限制,可能影响 scavenger 行为
    debug.SetMemoryLimit(100 * 1024 * 1024) // 100 MB
    // 模拟内存分配和释放
    data := make([]byte, 50*1024*1024)
    for i := range data {
        data[i] = 1
    }
    data = nil // 释放引用
    runtime.GC() // 触发垃圾回收
    time.Sleep(2 * time.Minute) // 等待 scavenger 运行
}

关于 cgroup 配置(如 memory.soft_limit_in_bytes)的影响:是的,即使设置 GODEBUG=madvdontneed=1,cgroup 内存限制也会改变内存压力检测,从而影响 scavenger 的触发时机。例如,较低的 soft_limit_in_bytes 可能使 scavenger 更早启动。但 Go 1.18 的改进降低了这种依赖性,使释放行为更一致。

总结:从 Go 1.16 到 1.18,scavenger 的积极度显著提高,RSS 释放延迟大幅减少。cgroup 配置仍会影响行为,但 Go 1.18 的优化使得在常见场景下释放更快速可靠。

回到顶部