问答

go获取结构体或map占了多少内存

作者:admin 2021-07-04 我要评论

最近有个需求要做个内存缓存,我想把数据放在结构体或者map中,但是一直没找到获取能占多少内存的方法,想设置一下最大内存,有什么方法能获取当前结构体占了多...

在说正事之前,我要推荐一个福利:你还在原价购买阿里云、腾讯云、华为云服务器吗?那太亏啦!来这里,新购、升级、续费都打折,能够为您省60%的钱呢!2核4G企业级云服务器低至69元/年,点击进去看看吧>>>)

最近有个需求要做个内存缓存,我想把数据放在结构体或者map中,但是一直没找到获取能占多少内存的方法,想设置一下最大内存,有什么方法能获取当前结构体占了多少内存嘛

###

map是一个特殊的“指针结构体”,为什么这样说呢,因为它其实并不是直接指向存放key-value的内存地址,而是有一个hmap结构体,在hmap内部才有一个结构体指向表内存地址。
所以,hmap是map的第一个组成部分
接下来在hmap内部找真正放数据的地方,代码链接

// A header for a Go map.
type hmap struct {
    // Note: the format of the hmap is also encoded in cmd/compile/internal/gc/reflect.go.
    // Make sure this stays in sync with the compiler's definition.
    count     int // # live cells == size of map.  Must be first (used by len() builtin)
    flags     uint8
    B         uint8  // log_2 of # of buckets (can hold up to loadFactor * 2^B items)
    noverflow uint16 // approximate number of overflow buckets; see incrnoverflow for details
    hash0     uint32 // hash seed
    buckets    unsafe.Pointer // array of 2^B Buckets. may be nil if count==0.
    oldbuckets unsafe.Pointer // previous bucket array of half the size, non-nil only when growing
    nevacuate  uintptr        // progress counter for evacuation (buckets less than this have been evacuated)

    extra *mapextra // optional fields
}

先定位到buckets,看注释知道,它是指向“桶数组”的地方,也就是指向表的指针。(oldbucketsmap在使用渐进式扩容是的备用桶数组,所以在map扩容的时期,严格来说,有两个放数据的地方,但是这里我们只说非扩容时期)
buckets内的成员是bmap结构体,

// A bucket for a Go map.
type bmap struct {
    // tophash generally contains the top byte of the hash value
    // for each key in this bucket. If tophash[0] < minTopHash,
    // tophash[0] is a bucket evacuation state instead.
    tophash [bucketCnt]uint8
    // Followed by bucketCnt keys and then bucketCnt elems.
    // NOTE: packing all the keys together and then all the elems together makes the
    // code a bit more complicated than alternating key/elem/key/elem/... but it allows
    // us to eliminate padding which would be needed for, e.g., map[int64]int8.
    // Followed by an overflow pointer.
}

从最好四行注释中,我们可以看到,它的排列方法并不是key/elem/key/elem/这种交替方法,而是把所有的key挤在一起,再把所有value挤在一起,这样虽然很复杂,但是可以消除数据结构的内存对齐导致的储存空间损耗问题。因此,使用元素大小乘以元素个数其实是不准确的算法,而且个数越多,结果差越多。

这里的buckets被定义为8,在代码链接中能找到,也就是一个桶内最多放8个哈希值高位相同的数据,在一般情况下哈希冲突不会很频繁,这里假设每个bmap只有一个元素,map的第二个组成元素是多个bmap,约等于元素个数的8倍

所以一个map的大小约等于一个hmap+8倍元素个数的bmap。bmap内又包括key和value。翻译一下,就是:
unsafe.Sizeof(hmap)+len(map)*8*(unsafe.Sizeof(key)+unsafe.Sizeof(value))

这是一个近似解,还存在更精确的算法。
有以下几个地方需要继续补充

  • 扩容时期存在的备用桶
  • 大于8个哈希冲突时的overflow
  • 内存对齐

感谢 @搬砖程序员带你飞 大佬的解疑

版权声明:本文转载自网络,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。本站转载出于传播更多优秀技术知识之目的,如有侵权请联系QQ/微信:153890879删除

相关文章
  • PHP的use 的 前提是包中的文件需要提前

    PHP的use 的 前提是包中的文件需要提前

  • 为什么分布式项目中需要分布式锁,而普

    为什么分布式项目中需要分布式锁,而普

  • mysql高效查询评论及回复内容,并且分

    mysql高效查询评论及回复内容,并且分

  • tp5 数据库查询  如何进行多对多查询

    tp5 数据库查询 如何进行多对多查询

腾讯云代理商
海外云服务器