Golang map 如何扩容

发布于 1周前 作者 gougou168 最后一次编辑是 5天前 来自 问答

Golang map 如何扩容

  1. 双倍扩容:扩容采取了一种称为“渐进式 ”的方式,原有的 key 并不会一 次性搬迁完毕,每次最多只会搬迁 2 个 bucket。

  2. 等量扩容:重新排列,极端情况下,重新排列也解决不了,map 存储就会蜕 变成链表,性能大大降低,此时哈希因子hash0 的设置,可以降低此类极 端场景的发生。

2 回复

在Go语言中,map 的内部结构是基于哈希表的,它会自动根据需要扩容以存储更多的键值对,而不需要开发者手动干预。但是,了解map是如何扩容的以及扩容的时机对于优化程序性能和理解Go的内部机制是有帮助的。

扩容机制

Go的map在内部使用一个数组来存储哈希桶(buckets),每个哈希桶可以存储多个键值对。当map中的元素数量超过其当前容量的加载因子(默认为6.5)时,map会自动扩容。扩容通常是将现有的buckets数量乘以2(在Go 1.18及以前版本中),并在新的buckets数组中重新分配键值对。从Go 1.18开始,引入了更复杂的扩容策略,旨在优化内存使用和性能。

示例代码

由于map的扩容是自动的,你不能直接控制或触发它,但可以通过添加元素来间接观察其行为。下面是一个简单的示例,演示了如何向map中添加元素,并观察扩容可能导致的内存分配变化(通过运行时统计信息)。

package main

import (
    "fmt"
    "runtime"
    "time"
)

func main() {
    m := make(map[int]int)

    // 初始时,map占用的内存
    var ms runtime.MemStats
    runtime.ReadMemStats(&ms)
    initialAlloc := ms.Alloc

    // 填充map以触发扩容
    for i := 0; i < 100000; i++ {
        m[i] = i
    }

    // 再次获取内存分配情况
    runtime.ReadMemStats(&ms)
    finalAlloc := ms.Alloc

    fmt.Printf("Initial allocation: %d bytes\n", initialAlloc)
    fmt.Printf("Final allocation: %d bytes\n", finalAlloc)
    fmt.Printf("Increase in allocation: %d bytes\n", finalAlloc-initialAlloc)

    // 注意:实际增加的内存量会因运行时环境和map内部实现细节而异

    // 为了演示,我们可以暂停一会儿,观察内存变化(在实际应用中,这通常不是必需的)
    time.Sleep(1 * time.Second)
}

注意:这个示例主要是为了展示如何观察map扩容可能导致的内存分配变化,而不是直接控制或触发扩容。在Go中,map的扩容是自动和内部处理的,你不需要(也不能)直接控制它。

结论

虽然不能直接控制map的扩容,但了解它的扩容机制可以帮助你更好地理解Go的内存使用和性能优化。当设计使用大量键值对的程序时,考虑map的扩容行为是一个好习惯。

回到顶部