Golangのメモリリークを分析して修正した方法

0
読了時間
Golangのメモリリークを分析して修正した方法
Glean Icon - Circular - White
GleanによるAIサマリー
  • Glean 氏は、Google Cloud Platform上で稼働しているGolangサービスでメモリリークが発生し、メモリが増加し、インスタンスが強制終了される原因となっていることを確認しました。
  • 彼らは継続的なプロファイリングデータとMemStatsロギングを使用して、問題がGolangによるMADV_FREEの使用に関連していることを確認しました。MADV_FREEは、コンテナ環境ではメモリをすぐにOSに戻しませんでした。
  • GODEBUG環境変数を「madvdontneed=1」に設定することで問題を解決し、安定したメモリフットプリントとパフォーマンスの向上につながりました。

Glean いくつかの問題を解決するための最新のクラウド専用アーキテクチャを構築しています ハード・エンタープライズ・サーチ そしてナレッジマネジメント関連の問題。パフォーマンスとリソースコストの最適化は非常に重要であり、興味深い技術的課題につながることがよくあります。これらの課題の1つをデバッグしたところ、同様の課題に取り組んでいる他の人に役立つかもしれない興味深い発見がありました。

Glean では、適度にメモリを大量に消費するサービスにGolangを使用しています。また、ほとんどのデプロイには Google クラウドプラットフォーム (GCP) を使用しています。このサービスは、を使用して App Engine フレキシブルインスタンスとして実行します。 カスタムランタイム Go 1.15を含むイメージ。Golangサービスで次のような興味深い動作が見られました。

メモリは徐々に増加し、上限に達し(この場合は AppEngine のリソース制限として 3 GB を使用していました)、インスタンスは強制終了されました。これはおそらくメモリ制限を超えていたためです。メモリグラフを見ると、絶え間ないランプアップはメモリリークのようなにおいがしました。

アプリケーションのメモリリークではない

ありがたいことに、このケースでは、アプリケーションでのメモリリークをデバッグするのは難しくありません。というのも、次を使用して継続的なプロファイリングデータにアクセスできるからです。 クラウドプロファイラー。これまで、Google Remote Procedure Call(gRPC)接続が閉じていないと、このような問題が発生することがありましたが、コンティニュアスプロファイラーを使用すると簡単にデバッグできました。特に、 フレームグラフこのような場合、プロファイラーのUIでは、特定のコールサイトで頻繁に使用されていることが明確に表示されます。この場合、それは起こっていませんでした。ただし、プロファイルで明らかになった興味深い点の1つは、平均ヒープサイズ(つまり、使用中のオブジェクト別)が約1.5G(つまり、アプリエンジンが認識していたメモリフットプリントの約2倍)であったことです。つまり、メモリはGolangランタイムによってどこかに保持されていたということです。次に考えたのは、これがメモリの断片化の問題かどうかということでした。なぜなら、Golangは 悪いことで知られている その面では。

フラグメンテーションケースではない

幸いなことに、断片化が原因ではないと結論付けるのもそれほど難しくありませんでした。定期的にログを記録するバックグラウンドスレッドを追加しました MemStats。断片化されたメモリの上限は、HeapInUse から HeapAlloc を減算することで簡単に求めることができます。特に、「HeapInUse から HeapAlloc を引くと、特定のサイズのクラスに割り当てられているが、現在は使用されていないメモリの量を推定できます。」この量はかなり小さく、私たちの場合は最大3MBでした。

また、HeapReleased の値がかなり大きかったのも興味深い点でした。私たちはもっと探し始めて出くわしました このスレッド 同様の問題について。

{{リッチテキストバナーコンポーネント}}

Golang/コンテナ環境の相互作用の問題

におけるポテンシャル理論 Golang問題スレッド Goがgo 1.12のデフォルトとしてMADV_FREEを使い始めたということです。つまり、メモリを直ちに OS に戻すことはできず、OS はメモリ不足を感じたときにこのメモリを再利用することを選択できます。ただし、戻ると コンテナの実装方法、これらは基本的に別の Cgroup で実行されているプロセスにすぎません。そのため、コンテナがメモリ制限に達して強制終了されても、OS はメモリ負荷を感じず、メモリを解放しない可能性があります。

幸いなことに、この動作を逆にして、代わりにMADV_DONTNEEDを使用するGolangデバッグフラグがあります。 デバッグする 環境変数を」マッドブドニード=1」。実際、go 1.16には これを使うように戻しました 現在はデフォルトです。この変更後のメモリグラフは、2Gではずっと良く、安定しているように見えます。

重要なポイント

  1. 証明 また、フレームグラフはアプリケーションのメモリリークを分析するのに非常に役立ちます。連続プロファイラーは、プロファイルの複数のスナップショットを見て、リークの原因をすばやく突き止めるのに非常に役立ちます。 クラウドプロファイラー GCPワークロードには間違いなく便利なツールです。
  2. MemStats ロギングは、潜在的な原因をより高いレベルで分析するのに役立ちます。特に、「HeapInUse から HeapAlloc を引いた値」は、メモリが無駄に消費されるフラグメンテーションの量を見積もる際の上限として使用できます。
  3. コンテナ内で1.12から1.15の間のgoを使用している場合は、設定することをお勧めします GoDebug では mdvdntneed=1 が必要ありません。 :-)
LLMとGPTで成り立つ相続人

LLMとGPTで成り立つ相続人

ジェネレーティブAIの改良がどのようにして現代のワークプレイス変革の最前線に立ったのか、そしてそれらを企業ビジネスのいくつかの主要分野に統合する最善の方法については、ホワイトペーパーをご覧ください。

LLMとGPTで成り立つ相続人
Work AI for all.
Get a Demo
CTA Section Background Shape

Work AI for All.

デモに申し込む
CTA BG