What is Soft Memory Limit?Hiroyuki Yagihashi @ Go 1.19 Release Party🎉
View Slide
自己紹介- Hiroyuki Yagihashi-ソフトウェアエンジニア- maintidx- Maintainability Indexを計測する静的解析ツール- Twitter: @yagipy_- GitHub: @yagipy- Blog: https://blog.yagipy.me
アジェンダ- Go GCの概要-背景- Soft Memory Limit
ゴール- Go GCの概要を理解する- Soft Memory Limit導入の背景と機能を理解する
Go GCの概要
GCとは-使わなくなったメモリを特定し再利用できるようにするシステム- Go GCはTracing GC & Concurrent Mark Sweep-大きくMarkフェーズとSweepフェーズに分かれる- Markフェーズ:アプリで使われているメモリオブジェクトをMark- Sweepフェーズ: MarkされていないオブジェクトをSweep- Stop The World(通称STW)が発生するhttps://en.wikipedia.org/wiki/Tracing_garbage_collection
GCの実行コスト- GCの実行によってCPUコストが増え、メモリ使用量が減る-主なCPUコスト- GCの実行コスト-ライブヒープのサイズに比例して増加するコスト(Markのコスト)- Sweepは高速であるため、コストを無視できるものとする-主なメモリ使用量-ライブヒープ:前回GCでMarkされたメモリ-ニューヒープ: Markフェーズ前に割り当てられた新しいヒープメモリ-ライブヒープになるか、解放されるかは分からない-実行すればするほどCPUコストが増え、メモリ使用量が減る-実行しなければしないほどCPUコストが減り、メモリ使用量が増えるhttps://tip.golang.org/doc/gc-guide#Understanding_costs
Go GCの実行タイミング•頻度(1.18以前)1.前回のGCから2分後2.前回GC後のライブヒープと同量をニューヒープが確保した時(※ デフォルト)- 2はruntime/debug.SetGCPercent / GOGCで変更可能-ニューヒープをどの程度確保したらGCを実行するかを比率で指定(デフォルトは100%)-前回のライブヒープを100%とするhttps://tip.golang.org/doc/gc-guide
GOGCと実行コストまとめ- GOGCが高い- GC実行頻度が低くなるため、CPUコストが減り、メモリ使用量が増える- GOGCが低い- GC実行頻度が高くなるため、CPUコストが増え、メモリ使用量が減る
背景
Go 1.18以前- GOGCがGCの動作を変更するために使用できる唯一のパラメータ-実際に使用できるメモリが有限であることを考慮したパラメータではない- GOGCは使用可能メモリ量は知らず、比率によってのみ動作が決定する
ライブヒープにスパイクが発生するケース- CPU使用率を低くするために、GOGCを高く設定したくてもできない-ライブヒープのスパイクを考慮して設定しなければならない-アプリによってはスパイクの予測が困難-予期せぬタイミングでOut Of Memoryが発生してしまう可能性がある
具体例: GOGC=100-使えるメモリが60MiBで、ライブヒープのスパイク時が30MiB、通常時は10~20MiBのアプリを実行している場合-利用可能なメモリがあるにもかかわらず、GOGCを100以上に増加させることはできない
具体例: GOGC=200-通常時(20MiB)は60MiBまで使えるようになるが、スパイクが発生すると90MiBになるためOut Of Memoryが発生する
Go 1.18以前の主な解決策-ヒープバラスト-アプリ起動時に大規模なメモリ割り当て(バラスト)を行い、一定以上のライブヒープを確保する-例(twitch)-プラットフォーム間で移植性がない-適切なヒープバラストのサイズを見つける必要がある-手動GC-通常はcgroups等でのメモリ制限を行いつつ、その値に従って手動でGCを行う-例-頻繁に呼び出すと、大きくパフォーマンスが低下してしまう可能性がある-アプリの進行に大きな影響を与える可能性があるため、十分な調査と検証が必要
問題点-ライブヒープにスパイクが発生するケースで、CPU使用率が必要以上に高くなってしまう
Soft Memory Limit
Soft Memory Limit-メモリ制限を設定することができる機能-設定したメモリ制限を超えないようにGCが実行される- runtime/debug.SetMemoryLimit / GOMEMLIMITで設定-整数+単位文字列を指定(例: “100B”、”100MiB”、”100MB”、etc…)- GOGCとGOMEMLIMITは連動している-スパイク時はメモリ制限の値で制御し、プログラムの残りの実行はGOGCによって設定された値で制御
ライブヒープにスパイクが発生するケース(設定前)- Soft Memory Limit設定前
ライブヒープにスパイクが発生するケース(設定後)- 60MiBを超えることなく、CPU使用率が低下
問題点-ライブヒープにスパイクが発生するケースで、CPU使用率が必要以上に高くなってしまう- Soft Memory Limitを設定することで解決 🎉
補足- Goヒープとランタイムが管理する全てのメモリが対象-他言語で管理されるメモリやOSが保持するメモリは対象外-特定のメモリ量のコンテナにWebサービスを展開する場合、5~10%の余裕を持たせて設定することを推奨
Soft Memory Limitに関連するTips- GOGC=off- GCデススパイラルとその対策
GOGC=off- Soft Memory Limitは機能する-メモリ制限を保つために必要な、最小限のGC回数を設定できる
GCデススパイラルとその対策- GCデススパイラル-ライブヒープが大きくなるにつれて、GC頻度が増加する-最終的には連続でGCが実行され、CPU使用率が増加し、アプリの進行に問題が発生する-リーキーバケット機構を使用して、GCのCPU使用率が50%以下になるようにGCアシストを無効化する- GCのCPU時間分が累積し、アプリのCPU時間分が放出されるバケットを持つ-バケットが満たされた場合(GCのCPU使用率を50%を超えた場合)、バケットが空になるまでgoroutineはGCアシストをしない- GCアシスト:メモリ確保時にMarkフェーズのお手伝いをする機能-リーキーバケット機構のコミット
まとめ- Go 1.19でSoft Memory Limitが追加された- GOMEMLIMITを設定することにより、メモリ制限ができるようになった- Go 1.18以前よりGCを制御しやすくなった
参考文献-リリースノート- Go GCガイド(Go 1.19で追加)- Soft memory limitプロポーザル- HACKING.md- GitHub issue- Go 1.19のメモリ周りの更新|フューチャー技術ブログ