Heim >Backend-Entwicklung >Golang >Sie haben vielleicht schon davon gehört, GC auf der Golang-Karte durchzuführen?
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
是怎么删除的?
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
。
这些函数的参数类型是具体的 int64
,mapdelete_fast64
跟原始的 delete
操作一样的,所以我们来看看 mapdelete
。
长图预警!!!
大致代码分析如上,具体代码就留给大家去阅读了。其实大致过程:
key
是否存在count--
所以你在大面积删除 key
,实际 map
存储的 key
是不会删除的,只是标记当前的key状态为 empty
。
其实出发点,和 mysql
的标记删除类似,防止后续会有相同的 key
插入,省去了扩缩容的操作。
但是这个对有些场景是不妥的,如果开发者在未来时间内都不会再插入相同的 key
,很可能会导致 OOM
。
所以针对以上情况,go-zero
开发了 safemap
。下面我们看看 safemap
是如何避免这个问题的?
直接从操作 safemap
中分析为什么要这么设计:
newmap
中map
是一个整体,所以 key
只能留一份所以为什么要设置两个 map
就很清楚了:
dirtyOld
作为存储主体,如果 delete
操作达到阈值,则会触发迁移。dirtyNew
作为暂存体,会在到达阈值时,存放部分 key/value
所以在迁移操作时,我们需要做的就是:将原先的 dirtyOld
清空,存储的 key/value 通过 for-range 重新存储到 dirtyNew
,然后将 dirtyNew
指向 dirtyOld
。
可能会有疑问:不是说
key/value
没有删除吗,只是标记了tophash=empty
其实在
for-range
过程中,会过滤掉tophash 的 key
这样就实现了不需要的 key 不会被加入到 dirtyNew
,进而不会影响 dirtyOld
Erstellen Sie die Karte neu
safemap
ist in go-zero
integriert. safemap
kann dies bis zu einem gewissen Grad vermeiden. Dann sehen wir uns zunächst an, wie die von go
nativ bereitgestellte map
gelöscht wird?
Native Map-Löschung
rrreeego 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! ! !
🎜🎜🎜🎜 Die allgemeine Codeanalyse ist wie oben beschrieben, und der spezifische Code kann von jedem gelesen werden. Tatsächlich ist der allgemeine Prozess: 🎜Schlüssel
vorhanden istcount--
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
: 🎜🎜🎜map
sind ein Ganzes, sodass nur eine Kopie von key
übrig bleiben kannmap
eingerichtet werden: 🎜dirtyOld
als Speichergegenstand, wenn der delete
Wenn der Vorgang den Schwellenwert erreicht, wird die Migration ausgelöst. dirtyNew
Als temporärer Speicher wird bei Erreichen des Schwellenwerts ein Teil des Schlüssels/Wertes
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🎜Auf diese Weise werden unnötige Schlüssel nicht entfernt zukey/value
nicht gelöscht wurde? Es wurde nur alstophash=empty
markiert 🎜🎜 🎜eigentlich werden inWährend des for-range
-Prozesses die Schlüssel vontophash herausgefiltert🎜
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!