map的容量的获取

在go语言中,有两个内建函数分别是len(),cap(),前者用于获取容器的具体内容个数,后者用于获取容器分配的容量大小,但是这个cap对象是不能获取到map具体分配的容量大小的。有没有办法获取到呢,办法是有的,且看下文。

首先我们先使用gdb调试工具,查看一下map对象的具体结构是什么样子的。

一个及其简单的代码如下:

package main
func main() { m := make(map[string]int) m["a"] = 1 m["b"] = 2 } 

接下来我们编译这个简单的代码,并进行调试

# go build -o test -gcflags="-N -l" main.go 这里goflags是编译时指定的参数, -N 表示禁止优化,-l表示禁止内联。 便于调试

使用gdb进行调试

# gdb test
(gdb) b main.main
Breakpoint 1 at 0x401000: file /media/sf_goproject/src/map.go, line 3.
(gdb) r # 开始运行
Starting program: /media/sf_goproject/src/test
(gdb) n
m := make(map[string]int)
(gdb) ptype m

type = struct hash<string, int> {

int count;

uint8 flags;

uint8 B;

uint32 hash0;

struct bucket<string, int> *buckets;

struct bucket<string, int> *oldbuckets;

uintptr nevacuate;

[2]*[]*runtime.bmap *overflow;

} *

从上面的调试可以看到map对象的数据结构,在golang的runtime包的的haspmap.go中有这个结构的详细介绍:

// A header for a Go map.
type hmap struct { // Note: the format of the Hmap is encoded in ../../cmd/internal/gc/reflect.go and // ../reflect/type.go. Don't change this structure without also changing that code! 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) 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) // If both key and value do not contain pointers and are inline, then we mark bucket // type as containing no pointers. This avoids scanning such maps. // However, bmap.overflow is a pointer. In order to keep overflow buckets // alive, we store pointers to all overflow buckets in hmap.overflow. // Overflow is used only if key and value do not contain pointers. // overflow[0] contains overflow buckets for hmap.buckets. // overflow[1] contains overflow buckets for hmap.oldbuckets. // The first indirection allows us to reduce static size of hmap. // The second indirection allows to store a pointer to the slice in hiter. overflow *[2]*[]*bmap } 



所以B代表了map的容量 既然我们知道了数据的的结构,便可根据结构得到容量的内容。代码示例如下。

 

package main

type hmap struct {
    count int 
    flags uint8
    B     uint8  
    hash0 uint32
    buckets    unsafe.Pointer 
    oldbuckets unsafe.Pointer 
}


func main() {
    m := make(map[string]string)
    c, b := getInfo(m)
    fmt.Println("count: ", c, "b: ", b)
    for i := 0; i < 10000; i++ {
        m[strconv.Itoa(i)] = strconv.Itoa(i)
        if i%200 == 0 {
            c, b := getInfo(m)
            cap := math.Pow(float64(2), float64(b))
            fmt.Printf("count: %d, b: %d, load: %f\n", c, b, float64(c)/cap)
        }
    }
    println("开始删除------")
    for i := 0; i < 10000; i++ {
        delete(m, strconv.Itoa(i))
        if i%200 == 0 {
            c, b := getInfo(m)
            cap := math.Pow(float64(2), float64(b))
            fmt.Println("count: ", c, "b:", b, "load: ", float64(c)/cap)
        }
    }


    debug.FreeOSMemory()
    c, b = getInfo(m)
    fmt.Println("释放后: ", "count: ", c, "b:", b)
}


func getInfo(m map[string]string) (int, int) {
    point := (**hmap)(unsafe.Pointer(&m))
    value := *point
    return value.count, int(value.B)
}

一些记录:
1. 在看许多文章中有说到map分配的键值被删除之后,内存是不会释放的。但是在我测试的过程中,发现内存是可以释放的。可能是版本的原因,测试的版本是1.7.1
2. map是非并发安全的,使用过程中需要自己去控制加锁。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值