# Golang #のマップ構造は、キー値ペアを削除するときに実際に削除されず、マークされています。では、キーと値のペアが増えると、大量のメモリの無駄が発生するのでしょうか?
まず第一に、答えは「はい」です。OOM が発生する可能性が非常に高く、これについては github.com/golang/go/issues/20135 で議論されています。一般的な意味は、大規模な map
では、delete
操作では実際にはメモリが解放されず、メモリ OOM が発生する可能性があるということです。
したがって、一般的なアプローチは、マップを再構築することです。 safemap
のコンテナ コンポーネントは go-zero
に組み込まれています。 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 tools apply -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
と同じように動作しますので、見てみましょうマップ削除
。
長い画像の警告! ! !
#一般的なコード分析は上記のとおりで、具体的なコードは誰でも読めるように残されています。実際、一般的なプロセスは次のとおりです。
key
をクエリします (存在する場合)count--
したがって、実際の広い領域の key
を削除します。 map
は保存されますが、key
は削除されず、現在のキーのステータスが empty
としてマークされるだけです。
実際、開始点は mysql
のマークの削除に似ており、これにより今後同じ key
が挿入されることがなくなり、拡張の必要がなくなります。そして縮小操作。
ただし、これは一部のシナリオでは不適切です。開発者が今後同じ key
を挿入しない場合、OOM
が発生する可能性があります。
そこで、上記の状況に対応して、go-zero
は safemap
を開発しました。 safemap
がこの問題をどのように回避するかを見てみましょう。
操作から直接、このように設計されている理由を分析します。 safemap
:
newmap
map
に配置されます。は全体であるため、key
は 1 つのコピーのみを保持できますしたがって、2 つの map
を設定する必要がある理由は非常に明白です。
dirtyOld
ストレージ プリンシパルとして、delete
操作がしきい値に達すると移行がトリガーされます。 dirtyNew
一時ストレージとして、しきい値に達すると、key/value
の一部が保存されます。 、移行操作中に行う必要があることは次のとおりです: 元の dirtyOld
をクリアし、保存されたキー/値を for-range を介して dirtyNew
に再度保存し、 dirtyNew
は dirtyOld
を指します。
このようにして不要なキーが実現されます。質問があるかもしれません:
##実際、key/value
は削除されないということですか?tophash=empty
# とマークされているだけです。for-range
プロセスでは、キー
tophash がフィルターで除外されます
dirtyNew には追加されず、
dirtyOld には影響しません。
star## の使用へようこそ# 私たちを応援してください!
以上がGolang マップ上で GC を実行することについて聞いたことがあるかもしれません。の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。