>백엔드 개발 >Golang >Golang 맵에서 GC를 수행하는 것에 대해 들어보셨나요?

Golang 맵에서 GC를 수행하는 것에 대해 들어보셨나요?

藏色散人
藏色散人앞으로
2021-08-13 14:26:462593검색

우선, 대답은 '예'입니다. OOM을 일으킬 가능성이 매우 높으며 이에 대한 논의가 있습니다: github.com/golang/go/issues/20135. 일반적인 의미는 대규모 에서 삭제 작업이 실제로 메모리를 해제하지 않고 메모리 OOM을 유발할 수 있다는 것입니다. 일반적인 접근 방식은 지도를 다시 작성하는 것입니다. safemap의 컨테이너 구성요소는 go-zero에 내장되어 있습니다. safemap은 이를 어느 정도 방지할 수 있습니다.

그럼 먼저 go에서 기본으로 제공하는 지도를 삭제하는 방법을 살펴볼까요? 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"

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

长图预警!!!

Golang 맵에서 GC를 수행하는 것에 대해 들어보셨나요?

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

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

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

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

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

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

safemap

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

Golang 맵에서 GC를 수행하는 것에 대해 들어보셨나요?

  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

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

Golang 맵에서 GC를 수행하는 것에 대해 들어보셨나요?네이티브 맵 삭제

rrreee

테스트 코드는 위와 같으며 go tool compile -S -N -l testmap.go | grep "CALL"을 전달할 수 있습니다.

rrreee

행 실행 12 delete, 실제 실행은 runtime.mapdelete_fast64입니다.

이 함수의 매개변수 유형은 특정 int64이며, mapdelete_fast64는 원래 delete 작업과 동일하므로 를 살펴보겠습니다. > 지도삭제.

mapdelete

긴 사진 경고! ! !

일반적인 코드 분석은 위와 같으며, 구체적인 코드는 누구나 읽을 수 있도록 남겨둡니다. 실제로 일반적인 프로세스는 다음과 같습니다. 🎜
  1. 동시 쓰기 방지를 위한 쓰기 보호
  2. 삭제할 가 존재하는지 쿼리
  3. 존재하는 경우 표시하세요 삭제 표시 만들기
  4. count--
🎜그래서 넓은 영역에서 를 삭제합니다. , 실제 저장된 는 삭제되지 않지만, 현재 키 상태는 비어있음으로 표시됩니다. 🎜🎜실제로 시작점은 mysql의 태그 삭제와 비슷합니다. 이는 나중에 동일한 가 삽입되는 것을 방지하여 확장 및 축소 작업이 필요하지 않게 해줍니다. 🎜🎜그러나 이는 일부 시나리오에서는 부적절합니다. 개발자가 나중에 동일한 를 다시 삽입하지 않으면 OOM이 발생할 가능성이 높습니다. 🎜🎜그래서 위 상황에 대응하여 go-zero에서는 safemap을 개발했습니다. safemap이 이 문제를 어떻게 방지하는지 살펴보겠습니다. 🎜🎜🎜🎜safemap🎜🎜 safemap 작업에서 직접 왜 이렇게 설계되었는지 분석해 보세요. 🎜🎜Golang 맵에서 GC를 수행하는 것에 대해 들어보셨나요?🎜
  1. 🎜삭제 임계값🎜을 미리 설정하세요. 트리거되면 새로운 사전 설정newmap
  2. 두 개의 map은 전체이므로 key의 사본 하나만 남을 수 있습니다.
🎜그래서 두 개의 map이 설정된 이유는 분명합니다: 🎜
  1. dirtyOld가 저장 주제로, 삭제가 작업이 임계값에 도달하면 마이그레이션이 트리거됩니다.
  2. dirtyNew 임시 저장소로 임계값에 도달하면 키/값의 일부가 저장됩니다.
따라서 마이그레이션 중에 작업 중에 해야 할 일은 🎜원본 dirtyOld를 지우고 for-range를 통해 저장된 키/값을 다시 dirtyNew에 저장한 다음입니다. dirtyNew 지점을 dirtyOld🎜로 변경하세요. 🎜
🎜질문이 있을 수 있습니다. 키/값이 삭제되지 않았다는 의미 아닌가요? 방금 tophash=empty로 표시되었습니다🎜🎜 🎜실제로 에서 for-range 프로세스 중에 tophash 의 키가 필터링됩니다.🎜
🎜이렇게 하면 불필요한 키가 필터링되지 않습니다. dirtyNew 에 추가되었으며 dirtyOld에는 영향을 미치지 않습니다. 🎜🎜🎜🎜🎜이것은 사실 구세대와 신세대의 가비지 수집 개념입니다. 🎜🎜자세한 구현 내용은 소스코드를 확인해주세요! 🎜🎜🎜🎜프로젝트 주소🎜🎜github.com/tal-tech/go-zero🎜🎜go-zero 사용을 환영하고 🎜star🎜 지원해 주세요! 🎜

위 내용은 Golang 맵에서 GC를 수행하는 것에 대해 들어보셨나요?의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
이 기사는 learnku.com에서 복제됩니다. 침해가 있는 경우 admin@php.cn으로 문의하시기 바랍니다. 삭제