首页 > 后端开发 > Golang > 正文

你或许听过对Golang map做GC?

藏色散人
发布: 2021-08-13 14:26:46
转载
2789人浏览过

golang 中的 map 结构,在删除键值对的时候,并不会真正的删除,而是标记。那么随着键值对越来越多,会不会造成大量内存浪费?

首先答案是会的,很有可能导致 OOM,而且针对这个还有一个讨论:github.com/golang/go/issues/20135。大致的意思就是在很大的 map 中,delete 操作没有真正释放内存而可能导致内存 OOM。

所以一般的做法:就是 重建map。而 go-zero 中内置了 safemap 的容器组件。safemap 在一定程度上可以避免这种情况发生。

那首先我们看看 go 原生提供的 map 是怎么删除的?

原生map删除

1  package main
2
3  func main() {
4      m := make(map[int]string, 9)
5      m[1] = "hello"
6      m[2] = "world"
7      m[3] = "go"
8
9      v, ok := m[1]
10     _, _ = fn(v, ok)
11
12     delete(m, 1)
13  }
14
15 func fn(v string, ok bool) (string, bool) {
16     return v, ok
17 }
登录后复制

测试代码如上,我们可以通过 go tool compile -S -N -l testmap.go | grep "CALL"

立即学习go语言免费学习笔记(深入)”;

0x0071 00113 (test/testmap.go:4)        CALL    runtime.makemap(SB)
0x0099 00153 (test/testmap.go:5)        CALL    runtime.mapassign_fast64(SB)
0x00ea 00234 (test/testmap.go:6)        CALL    runtime.mapassign_fast64(SB)
0x013b 00315 (test/testmap.go:7)        CALL    runtime.mapassign_fast64(SB)
0x0194 00404 (test/testmap.go:9)        CALL    runtime.mapaccess2_fast64(SB)
0x01f1 00497 (test/testmap.go:10)       CALL    "".fn(SB)
0x0214 00532 (test/testmap.go:12)       CALL    runtime.mapdelete_fast64(SB)
0x0230 00560 (test/testmap.go:7)        CALL    runtime.gcWriteBarrier(SB)
0x0241 00577 (test/testmap.go:6)        CALL    runtime.gcWriteBarrier(SB)
0x0252 00594 (test/testmap.go:5)        CALL    runtime.gcWriteBarrier(SB)
0x025c 00604 (test/testmap.go:3)        CALL    runtime.morestack_noctxt(SB)
登录后复制

执行第12行的 delete,实际执行的是 runtime.mapdelete_fast64

这些函数的参数类型是具体的 int64mapdelete_fast64 跟原始的 delete 操作一样的,所以我们来看看 mapdelete

mapdelete

长图预警!!!

de3a209fbb1370722f64a565ac9847e.png

大致代码分析如上,具体代码就留给大家去阅读了。其实大致过程:

  1. 写保护,防止并发写
  2. 查询要删除的 key 是否存在
  3. 存在则对其标志做删除标记
  4. count--

所以你在大面积删除 key ,实际 map 存储的 key 是不会删除的,只是标记当前的key状态为 empty

其实出发点,和 mysql 的标记删除类似,防止后续会有相同的 key 插入,省去了扩缩容的操作。

但是这个对有些场景是不妥的,如果开发者在未来时间内都不会再插入相同的 key ,很可能会导致 OOM

Notion Sites
Notion Sites

Notion 推出的AI网站构建工具,允许用户将 Notion 页面直接发布为完整网站。

Notion Sites 246
查看详情 Notion Sites

所以针对以上情况,go-zero 开发了 safemap 。下面我们看看 safemap 是如何避免这个问题的?

safemap

直接从操作 safemap 中分析为什么要这么设计:

e64bced9b741d07e774365c20f02bad.png

  1. 预设一个 删除阈值,如果触发会放到一个新预设好的 newmap
  2. 两个 map 是一个整体,所以 key 只能留一份

所以为什么要设置两个 map 就很清楚了:

  1. dirtyOld 作为存储主体,如果 delete 操作达到阈值,则会触发迁移。
  2. dirtyNew 作为暂存体,会在到达阈值时,存放部分 key/value

所以在迁移操作时,我们需要做的就是:将原先的 dirtyOld 清空,存储的 key/value 通过 for-range 重新存储到 dirtyNew,然后将 dirtyNew 指向 dirtyOld

可能会有疑问:不是说 key/value 没有删除吗,只是标记了 tophash=empty其实在 for-range 过程中,会过滤掉 tophash

这样就实现了不需要的 key 不会被加入到 dirtyNew,进而不会影响 dirtyOld

a70694d5b7e44618aadd71203f47e9f.png

这其实也就是垃圾回收的年老代和新生代的概念。

更多实现细节,可以查看源码!

项目地址

github.com/tal-tech/go-zero

欢迎使用 go-zero 并 star 支持我们!

以上就是你或许听过对Golang map做GC?的详细内容,更多请关注php中文网其它相关文章!

相关标签:
最佳 Windows 性能的顶级免费优化软件
最佳 Windows 性能的顶级免费优化软件

每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。

下载
来源:learnku网
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
最新问题
开源免费商场系统广告
热门教程
更多>
最新下载
更多>
网站特效
网站源码
网站素材
前端模板
关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号