MST
星途 面试题库

面试题:Go语言Map内存占用优化之预分配

当我们已知map大概会存储多少键值对时,如何通过预分配策略优化map的内存占用?请举例说明预分配与不预分配在性能和内存占用上的区别。
23.3万 热度难度
编程语言Go

知识考点

AI 面试

面试题答案

一键面试

1. 通过预分配策略优化map内存占用的方法

在许多编程语言中,如Go语言,当已知map大概会存储多少键值对时,可以在创建map时使用make函数进行预分配。例如在Go语言中:

// 预分配能存储1000个键值对的map
myMap := make(map[string]int, 1000) 

这样做可以避免在添加元素时频繁地进行内存重新分配和扩容操作,从而优化内存占用和性能。

2. 预分配与不预分配在性能和内存占用上的区别

性能方面

  • 预分配:由于减少了内存重新分配的次数,在插入大量元素时,预分配内存的map性能会更好。每次内存重新分配都涉及到内存的复制和调整,这会带来额外的开销。例如在下面这个Go语言的性能测试代码中:
package main

import (
    "fmt"
    "time"
)

func main() {
    start := time.Now()
    myMap := make(map[string]int, 1000000)
    for i := 0; i < 1000000; i++ {
        key := fmt.Sprintf("key%d", i)
        myMap[key] = i
    }
    elapsed := time.Since(start)
    fmt.Printf("预分配内存插入100万个元素耗时: %s\n", elapsed)

    start = time.Now()
    anotherMap := make(map[string]int)
    for i := 0; i < 1000000; i++ {
        key := fmt.Sprintf("key%d", i)
        anotherMap[key] = i
    }
    elapsed = time.Since(start)
    fmt.Printf("不预分配内存插入100万个元素耗时: %s\n", elapsed)
}

一般情况下,预分配内存的myMap插入元素的耗时会明显少于不预分配内存的anotherMap

  • 不预分配:在插入元素时,如果map的容量不足,会触发扩容操作。扩容操作会导致性能下降,因为它需要重新分配内存、复制旧数据到新的内存地址等操作。

内存占用方面

  • 预分配:在初始阶段,预分配的map会占用相对较多的内存,因为它预先分配了足够容纳预计数量键值对的空间。但随着元素的插入,内存增长相对平稳,因为减少了扩容带来的内存浪费(扩容时可能会多分配一些空间以减少后续扩容频率)。
  • 不预分配:初始内存占用较少,但随着元素的不断插入,每次扩容时可能会多分配一定比例的内存空间以应对未来的增长,这可能导致在某些阶段内存占用比预分配的情况要多,而且频繁的扩容可能导致内存碎片化,进一步影响内存的使用效率。