Heim > Backend-Entwicklung > Golang > Sie haben vielleicht schon davon gehört, GC auf der Golang-Karte durchzuführen?

Sie haben vielleicht schon davon gehört, GC auf der Golang-Karte durchzuführen?

藏色散人
Freigeben: 2021-08-13 14:27:12
nach vorne
2607 Leute haben es durchsucht

Golang wird beim Löschen des Schlüssel-Wert-Paares nicht tatsächlich gelöscht, sondern markiert. Wird es also zu einer großen Speicherverschwendung kommen, wenn es immer mehr Schlüssel-Wert-Paare gibt? Zuallererst lautet die Antwort: Ja, es ist sehr wahrscheinlich, dass es OOM verursacht, und es gibt eine Diskussion darüber: github.com/golang/go/issues/20135. Die allgemeine Bedeutung ist, dass in einer großen map der delete-Vorgang den Speicher nicht tatsächlich freigibt und zu Speicher-OOM führen kann.

Der allgemeine Ansatz lautet also: 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 }
Nach dem Login kopieren

测试代码如上,我们可以通过 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)
Nach dem Login kopieren

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

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

mapdelete

长图预警!!!

Sie haben vielleicht schon davon gehört, GC auf der Golang-Karte durchzuführen?

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

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

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

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

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

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

safemap

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

Sie haben vielleicht schon davon gehört, GC auf der Golang-Karte durchzuführen?

  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,进而不会影响 dirtyOldErstellen Sie die Karte neu

. Die Containerkomponente von safemap ist in go-zero integriert. safemap kann dies bis zu einem gewissen Grad vermeiden.

Sie haben vielleicht schon davon gehört, GC auf der Golang-Karte durchzuführen?Dann sehen wir uns zunächst an, wie die von go nativ bereitgestellte map gelöscht wird?

Native Map-Löschung

rrreee

Der Testcode ist wie oben, wir können go tool compile -S -N -l testmap.go | übergeben: <span class="header-link octicon octicon-link">rrreee</span>Zeile ausführen 12 <code>delete, die tatsächliche Ausführung ist runtime.mapdelete_fast64.

Die Parametertypen dieser Funktionen sind spezifisch int64, mapdelete_fast64 ist derselbe wie der ursprüngliche delete-Vorgang, also werfen wir einen Blick auf Mapdelete.

mapdeleteLange Bildwarnung! ! !

🎜🎜Sie haben vielleicht schon davon gehört, GC auf der Golang-Karte durchzuführen? 🎜🎜 Die allgemeine Codeanalyse ist wie oben beschrieben, und der spezifische Code kann von jedem gelesen werden. Tatsächlich ist der allgemeine Prozess: 🎜
  1. Schreibschutz, um gleichzeitiges Schreiben zu verhindern
  2. Abfragen, ob der zu löschende Schlüssel vorhanden ist
  3. Wenn es existiert, markieren Sie es. Machen Sie Löschmarkierungen
  4. count--
🎜So löschen Sie key in einem großen Bereich , die tatsächliche map Der gespeicherte Schlüssel wird nicht gelöscht, aber der aktuelle Schlüsselstatus wird als leer markiert. 🎜🎜Tatsächlich ähnelt der Ausgangspunkt dem Tag-Löschen von mysql, wodurch verhindert wird, dass derselbe key später eingefügt wird, wodurch Erweiterungs- und Kontraktionsvorgänge überflüssig werden. 🎜🎜Aber dies ist für einige Szenarien ungeeignet. Wenn der Entwickler in Zukunft nicht mehr denselben Schlüssel einfügt, führt dies wahrscheinlich zu OOM. 🎜🎜Als Reaktion auf die obige Situation entwickelte go-zero safemap. Mal sehen, wie safemap dieses Problem vermeidet? 🎜🎜🎜🎜safemap🎜🎜 Analysieren Sie, warum es so gestaltet ist, direkt aus der Operation safemap: 🎜🎜Sie haben vielleicht schon davon gehört, GC auf der Golang-Karte durchzuführen?🎜
  1. Legen Sie einen 🎜Löschschwellenwert🎜 fest, der bei Auslösung in einem neuen Preset platziert wirdnewmap
  2. Die beiden map sind ein Ganzes, sodass nur eine Kopie von key übrig bleiben kann
🎜Es ist also klar, warum zwei map eingerichtet werden: 🎜
  1. dirtyOld als Speichergegenstand, wenn der delete Wenn der Vorgang den Schwellenwert erreicht, wird die Migration ausgelöst.
  2. dirtyNew Als temporärer Speicher wird bei Erreichen des Schwellenwerts ein Teil des Schlüssels/Wertes
gespeichert . Also während der Migration Während des Betriebs müssen wir nur Folgendes tun: 🎜Löschen Sie den ursprünglichen dirtyOld, speichern Sie den gespeicherten Schlüssel/Wert erneut in dirtyNew über for-range und dann change dirtyNew zeigt auf dirtyOld🎜. 🎜
🎜Sie haben möglicherweise Fragen: Bedeutete das nicht, dass key/value nicht gelöscht wurde? Es wurde nur als tophash=empty markiert 🎜🎜 🎜eigentlich werden in Während des for-range-Prozesses die Schlüssel von tophash herausgefiltert🎜
🎜Auf diese Weise werden unnötige Schlüssel nicht entfernt zu dirtyNew hinzugefügt, was sich nicht auf dirtyOld auswirkt. 🎜🎜🎜🎜🎜Das ist eigentlich das Konzept der alten Generation und der neuen Generation der Müllabfuhr. 🎜🎜Weitere Implementierungsdetails finden Sie im Quellcode! 🎜🎜🎜🎜Projektadresse🎜🎜github.com/tal-tech/go-zero🎜🎜Willkommen bei go-zero und 🎜star🎜 unterstützt uns! 🎜

Das obige ist der detaillierte Inhalt vonSie haben vielleicht schon davon gehört, GC auf der Golang-Karte durchzuführen?. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Verwandte Etiketten:
Quelle:learnku.com
Erklärung dieser Website
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn
Beliebte Tutorials
Mehr>
Neueste Downloads
Mehr>
Web-Effekte
Quellcode der Website
Website-Materialien
Frontend-Vorlage