Golang垃圾回收机制详解

Golang垃圾回收机制详解 我有一个全局映射 duplicates,我在一个 for 循环中运行这个映射,每次我执行 duplicates = make(map[int]bool)。我想知道,当我们再次执行 duplicates = make(map[int]bool) 时,映射之前占用的内存是否会被释放?

3 回复

你好,欢迎!所以你在做类似这样的事情:

package main

var duplicates = make(map[int]bool)

func main()  {
	for {
		duplicates = make(map[int]bool)
	}
}

嗯,在这种情况下,垃圾回收器每次只会分配一个映射所需的最小内存量(仅哈希映射的根结构),并且它会以批次的形式释放每次分配,因为垃圾回收器会在特定的时间“周期性地”运行(参见垃圾回收算法)。所以答案是否定的,并非每次都会回收,那样做成本太高了。

更多关于Golang垃圾回收机制详解的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


我使用堆(heap)和跟踪(trace)性能分析工具(pprof)进行了测试。根据我的观察,内存会被重用,并且垃圾回收(GC)调用的开销并不大。这是代码。我使用了一个包含100000个元素的映射(map),并且没有动态改变其大小。但从堆分析中可以看到:

image

内存占用量本身并不算太大。如果我们查看跟踪信息,堆大小保持稳定,这表明垃圾回收器非常高效地回收了内存。

我收集了程序运行60秒内的统计数据。在我的机器上,垃圾回收的平均运行时间是 1,968,642 ns,在我看来这也不算太多。

总结一下,为你的映射分配的内存会在每次 make 调用时被重用。我建议,如果你知道映射的大致大小,请将其大小作为参数传递给 make 调用。

在 Go 语言中,当你执行 duplicates = make(map[int]bool) 时,会创建一个新的空映射,并将 duplicates 变量指向这个新映射。此时,之前映射占用的内存会变成垃圾回收的候选对象,但不会立即释放。

Go 的垃圾回收器(GC)采用并发标记-清除算法,会在堆内存达到一定阈值或到达特定时间间隔时自动触发。当 GC 运行时,它会标记所有从根对象(如全局变量、栈上的变量等)可达的对象,而之前那个映射由于不再被任何变量引用,会被标记为不可达,从而在清除阶段被回收。

因此,之前映射占用的内存会在垃圾回收器运行时被释放,但具体时机由 GC 自动管理。你可以通过 runtime.GC() 强制触发一次 GC,但不建议在生产环境中频繁使用。

示例代码:

package main

import (
    "fmt"
    "runtime"
)

var duplicates map[int]bool

func main() {
    // 第一次分配
    duplicates = make(map[int]bool)
    duplicates[1] = true
    fmt.Printf("初始映射: %v\n", duplicates)

    // 分配新映射,旧映射成为垃圾
    duplicates = make(map[int]bool)
    fmt.Println("已分配新映射")

    // 可选:强制触发垃圾回收(仅用于演示)
    runtime.GC()

    // 使用新映射
    duplicates[2] = true
    fmt.Printf("新映射: %v\n", duplicates)
}

在这个例子中,第一次创建的映射在第二次执行 make 后不再被引用,会在 GC 运行时被回收。内存释放是自动的,无需手动干预。

回到顶部