首页 > 其他分享 >go map gc的测试代码

go map gc的测试代码

时间:2023-06-05 16:23:04浏览次数:42  
标签:map printMemStats gc 内存 intMap go GC runtime


**本文章由github copilot协助生成** 遇到一个离职同事写的代码,如下:
```go package mapGC func mapGc() { lock := sync.Mutex{} go func() { for { time.Sleep(12 * time.Hour) tmp := make(map[string]interface{}) lock.Lock() tmp = GlobalMap GlobalMap = nil GlobaMap = tmp lock.Unlock() } }() } ``` 我们就很怀疑, go不是有自动GC吗? 为什么还要再写个手动gc? 于是就开始研究, 为什么要这么写, 有什么好处, 有什么坏处, 有没有更好的写法? 我们知道, 如果map里面的kv被删除后, 如果kv没有被外界引用, 在一定情况下go会进行自动gc,所以kv所占用的内存不需要用上面的形式进行释放.
map内部实现是基于hash, 参考文章:https://blog.csdn.net/cyq6239075/article/details/106047992?utm_medium=distribute.pc_relevant.none-task-blog-2~default~baidujs_baidulandingword~default-4-106047992-blog-116662891.235%5Ev36%5Epc_relevant_default_base3&spm=1001.2101.3001.4242.3&utm_relevant_index=7 在看了上面的文章之后, 怀疑这段代码想删除的其实是map中多余的bucket. 但是这个bucket是不是真的会占用内存呢?其实是会的. map的底层实现是一个数组, 数组的每个元素是一个bucket, bucket中存储了key, value, 以及一个指向下一个bucket的指针. 如果map中的key很多, 但是value很少, 那么就会造成很多bucket中的value都是nil, 这样就会造成内存的浪费. 所以这段代码的目的是, 每隔12小时, 将map中的bucket中的value为nil的bucket删除掉, 从而减少内存的浪费.
但是这段代码有一个问题, 就是在删除bucket的时候, 会造成map的不可用, 因为在删除bucket的时候, 会对map进行加锁, 这样在删除的时候, 其他的goroutine就无法对map进行读写了. 所以这段代码的目的是好的, 但是实现的方式不好.
首先这段代码在我们的业务中是没有太大必要的, 因为我们业务属于周期性比较强的场景, 每晚都会有大量的数据写入, 所以不会出现key很多, value很少的情况. 其次有人可能会有疑问, 如果v比较大, 那么map size比较大的情况下, 也会占用比较大的内存, 其实不会的, 因为go在实现map的时候,如果sizeof(v)很大, 那么go会将v存储在heap中, 然后在map中存储的是v的指针, 所以map的size不会很大. 最后附录上我写的一个测试代码, 用来测试map的内存占用情况.
```go package main
import ( "log" "math/rand" "runtime" "unsafe" )
type T struct { a int b int c int d int str string balance [500000]float32 }
var intMap map[int]T
func getInfo(m map[string]string) (int, int) { point := (**T)(unsafe.Pointer(&m)) value := *point return value.a, int(value.b) }
func main() { printMemStats("main") // main:分配的内存 = 106KB, GC的次数 = 0 // 添加1w个map值 intMap = make(map[int]T, 100000) runtime.GC() printMemStats("初始化") // 分配的内存 = 7997KB, GC的次数 = 2 for i := 0; i < 100000; i++ { t := T{} intMap[rand.Intn(2147483647)] = t }
// 手动进行gc操作 runtime.GC() // 再次查看数据 printMemStats("增加map数据后") // 分配的内存 = 8789KB, GC的次数 = 3 for k := range intMap { delete(intMap, k) } log.Print("map.len:", len(intMap)) printMemStats("1.删除map数据后") //分配的内存 = 8790KB, GC的次数 = 3 // 再次进行手动GC回收 runtime.GC() printMemStats("2.删除map数据后并且gc后.") //分配的内存 = 8008KB, GC的次数 = 4 for i := 0; i < 10000; i++ { t := T{} intMap[rand.Intn(2147483647)] = t } for k := range intMap { delete(intMap, k) } runtime.GC() printMemStats("3.删除map数据后并且gc后.") // // for cnt := 0; cnt < 10000; cnt++ { // // log.Println("删除前数组长度:", len(intMap)) // // for i := 0; i < 100000; i++ { // // delete(intMap, i) // // } // for k := range intMap { // delete(intMap, k) // } // // log.Println("删除后数组长度:", len(intMap))
// // 再次进行手动GC回收 // // runtime.GC() // // printMemStats("删除map数据后")
// // for i := 0; i < 100000; i++ { // // intMap[i] = i // // } // for i := 0; i < 100000; i++ { // t := T{ // a: i, // } // intMap[rand.Intn(2147483647)] = t // } // // if cnt == 9999 { // // printMemStats("---------------重新添加·map数据后") // // } // } // printMemStats("---------------重新添加·map数据后") // runtime.GC() // printMemStats("----手动gc后")
// // 设置为nil进行回收 // intMap = nil // runtime.GC() // printMemStats("设置为nil后") }
func printMemStats(mag string) { var m runtime.MemStats runtime.ReadMemStats(&m) log.Printf("%v:分配的内存 = %vKB, GC的次数 = %v\n", mag, m.Alloc/1024, m.NumGC) } ```



标签:map,printMemStats,gc,内存,intMap,go,GC,runtime
From: https://www.cnblogs.com/micoblog/p/17458103.html

相关文章

  • Leangoo领歌Scrum工具实施多团队规模化敏捷Scrum of Scrums
    ​多团队大规模敏捷的场景定义:多个敏捷团队开发同一个大型产品,几十人,甚至几百人开发一个产品或解决方案。在Leangoo领歌中创建多团队大规模敏捷项目:多团队规模化敏捷的项目结构: 在Leangoo企业中创建项目,项目类型选择“敏捷开发”,项目模板选择“多团队大规模敏捷开发”。创......
  • GCC常用工具及其命令行
       ......
  • java实现:根据 map的value值获取key值
    /***根据map的value值获取key值*@parammap*@paramvalue*@return*/publicstaticStringgetKeyFromValue(Map<String,String>map,Stringvalue){StringmKey="";Set<String>keySet=......
  • 递归获取省市区的边界信息文件,用于echarts的map地图
    数据来源为阿里云,单个省市区信息可直接在这里面下载:http://datav.aliyun.com/portal/school/atlas/area_selector#&lat=30.332329214580188&lng=106.72278672066881&zoom=3.5由于需求需要点击省份里面的城市就展示新的城市的地图,所以需要把所有省市区的边界信息json全部下载下来......
  • 【IDE】Chrome 在其他机器登陆 Google 账号,没有退出,怎么及时保证账号及数据安全?
    Chrome浏览器,再配合Google账号,确实给我们带来了很多方便比如:书签同步,扩展插件同步,数据同步但是,当我们在别人的机器上登录Google账号后,如何及时保障账号安全呢?有人说,这有什么担心的?是,正常来说我们不需要操这份心。就怕碰到极端的人,或者别人的误操作,导致我们Google账号的......
  • 传奇开服架设教程--GeeM2与GomM2引擎通用假人脚本
    开新区注意事项:1、新区无任何玩家数据下可以运行“MirServer\假人行会初始化”目录下的“点我初始化假人行会.Bat”程序2、默认假人后台管理密码为:www.idc02.com假人修改说明:市面上的版本千变万化,不可能统一全部数据,以下就是可以随版本修改的自定义假人假人等级,装备修改路径:“Mir......
  • go语言切片
    特性长度可变、内容可变、引用类型、底层基于数组定义vars1[]int//长度、容量为0的切片,零值vars2=[]int{}//长度、容量为0的切片,字面量定义vars3=[]int{1,3,5}//字面量定义,长度、容量都是3vars4=make([]int,0)//长......
  • Java建造者模式,用代码体验LEGO的快乐
    前言本文主要讲述Java设计模式中的建造者模式,文中使用通俗易懂的案例,使你更好的学习本章知识点并理解原理,做到有道无术。一.什么是建造者模式建造者模式是23种设计模式中创建型模式的一种,它关注的是对象创建的步骤,将一个复杂的对象抽象出来,一步步地把一个复杂的对象创建出来。......
  • 数据万象 | AIGC 存储内容安全解决方案
    AIGC(人工智能生产内容)已经成为与PGC(专业生产内容)、UGC(用户生产内容)并驾齐驱的内容生产方式。由于AI的特性,AIGC在创意、个性化、生产效率等方面具有独特的优势,这些优势可以使得高质量的内容制作更简单,但也会帮助恶意份子更高效地炮制违法违规内容。数据万象从AIGC的输入、生产......
  • gcc编译器犯病怎么回事?求大佬看看
    犯病前:犯病后:源码:1#include<stdio.h>2main(){3//printf("欢迎使用电子通讯录\n");4FILE*fp=fopen("naph.txt","r");5charbuf[11];6inti,j,k,n,n2=0,n3=0,i2;7while(fgets(buf,1024,fp)){n++;}8......