Slide 1

Slide 1 text

What is Soft Memory Limit? Hiroyuki Yagihashi @ Go 1.19 Release Party 🎉

Slide 2

Slide 2 text

自己紹介 - Hiroyuki Yagihashi - ソフトウェアエンジニア - maintidx - Maintainability Index を計測する静的解析ツール - Twitter: @yagipy_ - GitHub: @yagipy - Blog: https://blog.yagipy.me

Slide 3

Slide 3 text

アジェンダ - Go GC の概要 - 背景 - Soft Memory Limit

Slide 4

Slide 4 text

ゴール - Go GC の概要を理解する - Soft Memory Limit 導入の背景と機能を理解する

Slide 5

Slide 5 text

Go GCの概要

Slide 6

Slide 6 text

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

Slide 7

Slide 7 text

GCの実行コスト - GC の実行によって CPU コストが増え、メモリ使用量が減る - 主な CPU コスト - GC の実行コスト - ライブヒープのサイズに比例して増加するコスト (Mark のコスト ) - Sweep は高速であるため、コストを無視できるものとする - 主なメモリ使用量 - ライブヒープ : 前回 GC で Mark されたメモリ - ニューヒープ : Mark フェーズ前に割り当てられた新しいヒープメモリ - ライブヒープになるか、解放されるかは分からない - 実行すればするほど CPU コストが増え、メモリ使用量が減る - 実行しなければしないほど CPU コストが減り、メモリ使用量が増える https://tip.golang.org/doc/gc-guide#Understanding_costs

Slide 8

Slide 8 text

Go GCの実行タイミング•頻度(1.18以前) 1. 前回の GC から 2 分後 2. 前回 GC 後のライブヒープと同量をニューヒープが確保した時 ( ※ デフォルト ) - 2 は runtime/debug.SetGCPercent / GOGC で変更可能 - ニューヒープをどの程度確保したら GC を実行するかを比率で指定 ( デフォルトは 100%) - 前回のライブヒープを 100% とする https://tip.golang.org/doc/gc-guide

Slide 9

Slide 9 text

GOGCと実行コストまとめ - GOGC が高い - GC 実行頻度が低くなるため、 CPU コストが減り、メモリ使用量が増える - GOGC が低い - GC 実行頻度が高くなるため、 CPU コストが増え、メモリ使用量が減る

Slide 10

Slide 10 text

背景

Slide 11

Slide 11 text

Go 1.18以前 - GOGC が GC の動作を変更するために使用できる唯一のパラメータ - 実際に使用できるメモリが有限であることを考慮したパラメータではない - GOGC は使用可能メモリ量は知らず、比率によってのみ動作が決定する

Slide 12

Slide 12 text

ライブヒープにスパイクが発生するケース - CPU 使用率を低くするために、 GOGC を高く設定したくてもできない - ライブヒープのスパイクを考慮して設定しなければならない - アプリによってはスパイクの予測が困難 - 予期せぬタイミングで Out Of Memory が発生してしまう可能性がある

Slide 13

Slide 13 text

具体例: GOGC=100 - 使えるメモリが 60MiB で、ライブヒープのスパイク時が 30MiB 、通常時は 10~20MiB の アプリを実行している場合 - 利用可能なメモリがあるにもかかわらず、 GOGC を 100 以上に増加させることはできない

Slide 14

Slide 14 text

具体例: GOGC=200 - 通常時 (20MiB) は 60MiB まで使えるようになるが、スパイクが発生すると 90MiB にな るため Out Of Memory が発生する

Slide 15

Slide 15 text

Go 1.18以前の主な解決策 - ヒープバラスト - アプリ起動時に大規模なメモリ割り当て ( バラスト ) を行い、一定以上のライブヒープを確保する - 例 (twitch) - プラットフォーム間で移植性がない - 適切なヒープバラストのサイズを見つける必要がある - 手動 GC - 通常は cgroups 等でのメモリ制限を行いつつ、その値に従って手動で GC を行う - 例 - 頻繁に呼び出すと、大きくパフォーマンスが低下してしまう可能性がある - アプリの進行に大きな影響を与える可能性があるため、十分な調査と検証が必要

Slide 16

Slide 16 text

問題点 - ライブヒープにスパイクが発生するケースで、 CPU 使用率が必要以上に高くなってし まう

Slide 17

Slide 17 text

Soft Memory Limit

Slide 18

Slide 18 text

Soft Memory Limit - メモリ制限を設定することができる機能 - 設定したメモリ制限を超えないように GC が実行される - runtime/debug.SetMemoryLimit / GOMEMLIMIT で設定 - 整数 + 単位文字列を指定 ( 例 : “100B” 、 ”100MiB” 、 ”100MB” 、 etc…) - GOGC と GOMEMLIMIT は連動している - スパイク時はメモリ制限の値で制御し、プログラムの残りの実行は GOGC によって設定された値で制 御

Slide 19

Slide 19 text

ライブヒープにスパイクが発生するケース(設定前) - Soft Memory Limit 設定前

Slide 20

Slide 20 text

ライブヒープにスパイクが発生するケース(設定後) - 60MiB を超えることなく、 CPU 使用率が低下

Slide 21

Slide 21 text

問題点 - ライブヒープにスパイクが発生するケースで、 CPU 使用率が必要以上に高くなってし まう - Soft Memory Limit を設定することで解決 🎉

Slide 22

Slide 22 text

補足 - Go ヒープとランタイムが管理する全てのメモリが対象 - 他言語で管理されるメモリや OS が保持するメモリは対象外 - 特定のメモリ量のコンテナに Web サービスを展開する場合、 5~10% の余裕を持たせ て設定することを推奨

Slide 23

Slide 23 text

Soft Memory Limitに関連するTips - GOGC=off - GC デススパイラルとその対策

Slide 24

Slide 24 text

GOGC=off - Soft Memory Limit は機能する - メモリ制限を保つために必要な、最小限の GC 回数を設定できる

Slide 25

Slide 25 text

GCデススパイラルとその対策 - GC デススパイラル - ライブヒープが大きくなるにつれて、 GC 頻度が増加する - 最終的には連続で GC が実行され、 CPU 使用率が増加し、アプリの進行に問題が発生する - リーキーバケット機構を使用して、 GC の CPU 使用率が 50% 以下になるように GC アシ ストを無効化する - GC の CPU 時間分が累積し、アプリの CPU 時間分が放出されるバケットを持つ - バケットが満たされた場合 (GC の CPU 使用率を 50% を超えた場合 ) 、バケットが空になるまで goroutine は GC アシストをしない - GC アシスト : メモリ確保時に Mark フェーズのお手伝いをする機能 - リーキーバケット機構のコミット

Slide 26

Slide 26 text

まとめ - Go 1.19 で Soft Memory Limit が追加された - GOMEMLIMIT を設定することにより、メモリ制限ができるようになった - Go 1.18 以前より GC を制御しやすくなった

Slide 27

Slide 27 text

参考文献 - リリースノート - Go GC ガイド (Go 1.19 で追加 ) - Soft memory limit プロポーザル - HACKING.md - GitHub issue - Go 1.19 のメモリ周りの更新 | フューチャー技術ブログ