Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
Go の GC の不得意な部分を克服したい
Search
Sponsored
·
Your Podcast. Everywhere. Effortlessly.
Share. Educate. Inspire. Entertain. You do you. We'll handle the rest.
→
鹿
December 15, 2024
Programming
1.6k
4
Share
Go の GC の不得意な 部分を克服したい
Kyoto.go #56 オフライン忘年LT会の発表資料です。
https://kyotogo.connpass.com/event/335437/
鹿
December 15, 2024
More Decks by 鹿
See All by 鹿
なぜselectはselectではないのか
taiyow
1
440
Golang と Erlang
taiyow
8
2.2k
Other Decks in Programming
See All in Programming
AI時代の仕事技芸論 — ソフトウェア開発で「遊ぶように働く」職人的熟達のすすめ
kuranuki
1
400
Oxlintはいかにしてtsgolintのlint ruleを呼び出しているのか
syumai
2
950
Stage 3 Decorators でできること / できないこと / TSKaigi 2026
susisu
1
1.2k
TypeSpec で繋ぐ複数プロダクトの型安全
maroon8021
1
260
ReactとSvelteのその先、Ripple-TS / Beyond React and Svelte: Ripple-TS
ssssota
3
1.4k
Sans tests, vos agents ne sont pas fiables
nabondance
0
160
3Dシーンの圧縮
fadis
1
430
AIエージェントと協働するCLI開発 — BunとOpenClawで学んだこと
yoshikouki
1
220
バックエンドにElysiaJSを採用して気付いた、良い点・悪い点
wanko_it
1
180
Transactional Change Stream Processing With Debezium and Apache Flink
gunnarmorling
1
140
[BalkanRuby 2026] Drop your app/services!
palkan
3
710
CLIであることを活かしたGitHub Copilot CLI活用術 / GitHub Copilot CLI Pro Tips & Tricks
nao_mk2
1
1.1k
Featured
See All Featured
Helping Users Find Their Own Way: Creating Modern Search Experiences
danielanewman
31
3.2k
Public Speaking Without Barfing On Your Shoes - THAT 2023
reverentgeek
1
400
Stop Working from a Prison Cell
hatefulcrawdad
274
21k
Refactoring Trust on Your Teams (GOTO; Chicago 2020)
rmw
35
3.5k
Into the Great Unknown - MozCon
thekraken
41
2.5k
How to Create Impact in a Changing Tech Landscape [PerfNow 2023]
tammyeverts
55
3.4k
The Straight Up "How To Draw Better" Workshop
denniskardys
239
140k
The Spectacular Lies of Maps
axbom
PRO
1
770
Rails Girls Zürich Keynote
gr2m
96
14k
Kristin Tynski - Automating Marketing Tasks With AI
techseoconnect
PRO
0
260
How to Ace a Technical Interview
jacobian
281
24k
Taking LLMs out of the black box: A practical guide to human-in-the-loop distillation
inesmontani
PRO
3
2.2k
Transcript
Go の GC の不得意な 部分を克服したい 鹿 @mizushika1
GC とは • Garbage Collection • 作成したヒープを自動で回収してくれる仕組み • ヒープ →
プログラムが動的に確保するメモリのうち、スタックじゃない方 • 関数をまたいだスコープを持つとか、 スタックに乗せられない大きいサイズを確保するときに使う • Runtime 上で動く言語には大抵ある • 無いのは、C/C++, Rust, Zig, Ada など • イメージで言うと、 • 自分が散らかしている部屋をときどき片付けて ゴミを捨ててくれるお手伝いさん
Go の GC • Full mark and sweep • mark:
ヒープ領域全体を検査して、 使われているものから辿れないものを見つける • 検査漏れしないように mark 中はすべての goroutine を停止する → STW (Stop-the-world) • sweep: 使われていないものを解放する • こちらは並行処理可能、プロセスのCPU能力の25%まで使う • 『効率的なGo』 5.5.3 ガベージコレクションが詳しい • イメージで言うと、 • 毎日の掃除で引き出しやタンスの中のものを全部チェックしているサイコパス
世代別 GC というものも有る • GC をする対象を young と old に分けて
GC の頻度を分ける • 普段は young の方だけ GC を実施 • old はときどき or メモリが不足したときだけ実行 • 大抵のオブジェクトは、短命と長命に分かれる傾向がある • すぐ死ぬやつはすぐ死ぬが、死なないやつはずっと生き残る • old は毎回 GC してもほぼ空振りするので頻度を分ける • Go 以外の大抵の GCed 言語が採用 • イメージで言うと、 • 世代別GCは引き出しやタンスの中は大掃除のときだけチェックして、 普段は机や床を片付けるという普通の掃除
Go の GC の得意/不得意 • 得意: • 確保したヒープが早期に解放されるケース • REST
API のようなアクセスがすぐ終了するようなサービス • 一回実行するだけの CLI ツール • 不得意: • gRPC や WebSocket などを使って常時接続するサービス • 1時間以上などの長寿命のコネクション用のヒープが GCで毎回チェックされる • ※Go の GC がリリースごとに改善されているのは知っているが、 それでも常時接続サービスには厳しい面がある • ヒープオブジェクトが1億個あると STW が 1ms 近くになる • どうにか改善(GC負荷を軽減)したい
強い味方1: arena • GC 対象外の特別なヒープ領域を作れる • Go 1.20 に EXPERIMENTAL
で導入された • proposal: arena: new package providing memory arenas #51317 https://github.com/golang/go/issues/51317 • メリット • 長寿命オブジェクトを作っても GC の負荷が増えない • 自力でメモリ確保 (malloc) して割り当てるのに比べてアロケーションが楽 • デメリット • Arena 単位の管理になる • サイズは最低 8MB • Arena 全体の一括解放しかできない • 「長寿命のコネクション単位で arena を作ろう」と思っても、使用サイズが 8MB 未満だと、 メモリ効率が犠牲になる
強い味方1: arena % GOEXPERIMENT=arenas go build … var ar =
arena.NewArena() func allocate(size int) (string, []int) { str := arena.New[string](ar) is := arena.MakeSlice[int](ar, 0, size) return str, is ) // myArena.Free()
強い味方2: sync.Pool • メモリプールを作る標準パッケージ • プールから確保 (Get) して、使い終わったらプールに戻す (Put) •
毎回オブジェクトを生成して GC で回収されるのではなく、同じオブジェクトを使 いまして生成と回収の回数を減らす仕組み • ただし、毎回 GC 対象になるのは • これを、Pool の確保関数内で arena から取得してあとは Pool 側で管理することで、 解放が不要になり arena の「GC の負荷を増やさない」恩恵だけを受けられる • ただし Put を忘れると真の意味でのメモリリークになるので注意
強い味方2: sync.Pool const bufSize = 1024 var ( ar =
arena.NewArena() mu = sync.Mutex{} bufPool = sync.Pool{ New: func() any { mu.Lock() defer mu.Unlock() return arena.MakeSlice[byte](are, 0, bufSize) }, } } func foo() { buf := bufPool.Get().([]byte) defer bufPool.Put(buf) …
まとめ • Go の GC は様々なバランスを考慮した上で世代別 GC を採用していない • それだと長寿命のヒープオブジェクトが多いケースが不得意(GC
負荷が高い) • 回避策として、 arena でヒープを切り出して、sync.Pool でメモリプールを作ると 良さそう • とはいえ arena がまだ EXPERIMENTAL なので使い勝手は未知
おわり
補足:なぜ Go は世代別 GC を採用しないのか • “Getting to Go: The
Journey of Go's Garbage Collector” が参考になる https://go.dev/blog/ismmkeynote • 2018 年の ISMM (メモリ管理関連の学会?) のキーノート • 発表者の Rick Hudson 氏は Google の Go チームで GC とruntime の研究開発者 • 理由: Write Barrier が十分には早くないため • young と old の間の参照を追跡するために write barrier が必要になる • write barrier の遅延は常に(GC期間外にも)かかるので、ヒープが大きくなるほど(GC回数が 減るほど)相対的にコストが大きくなる、が現状では write barrier は速度面で不利 • つまり:世代別 GC の効果は Go では薄い • 世代別 GC を導入すると GC 中の STW を短縮できるが、Go ではこれは現状は大きな問題に なっていない • 世代別 GC を導入しても GC のスループットは向上しない
補足:arena はいつ標準になるの? • Go 1.20 のリリースは 2023/02 … 2年前? まだ
EXPERIMENTAL なの? • 答え:“Likely never” (ほぼ確実にならない) • https://github.com/golang/go/issues/51317#issuecomment-1676334535 • use-after-free 問題のせい • 通常のポインタから arena 内部のデータを指してから arena を解放すると…?
ほんとうにおわり