メインコンテンツまでスキップ

GC.Collect乱用がもたらす落とし穴

はじめに

C#を使った開発では、メモリ管理を.NETランタイムに任せることで、効率的かつ安全なプログラミングが可能になります。その中心にあるのが ガベージコレクション(GC) です。しかし、GC.Collect()を乱用すると、かえってパフォーマンスを悪化させることがあります。本記事では、その理由と正しい使い方について解説します。


なぜGC.Collect()の乱用が問題なのか?

GC.Collect()は、ガベージコレクションを強制的に実行するメソッドです。一見便利に見えますが、以下のような問題を引き起こす可能性があります。

  • .NETランタイムの最適化を妨げる    GCは世代別にメモリを管理し、最適なタイミングで回収を行います。強制的なGCはこの最適化を無視します。

  • パフォーマンスへの悪影響    GCは重い処理であり、頻繁に呼び出すとCPU使用率が上がり、アプリケーションが一時停止することもあります。

  • 予測不能な動作    メモリの解放タイミングが不自然になり、他の処理に影響を与える可能性があります。

特に、ファイナライズ待ちのオブジェクトも解放するために下記のような呼び出しを行うコードを見かけますが、GC.WaitForPendingFinalizersメソッドはSTAスレッドの場合COMのメッセージポンプを実行する (参考情報) ため、非常に多くの時間待機させられる可能性があります。

GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();

よくある誤用パターン

以下のようなケースでGC.Collect()を使うのは避けるべきです。

  • 大量のオブジェクトを扱った後に毎回呼び出す
  • Dispose()メソッドの中でGC.Collect()を呼ぶ
  • メモリリーク対策として安易に使用する

正しい使い方と代替手段

GC.Collect()を使うべきケース

  • メモリ使用量のプロファイリング時
  • 明示的にGCの影響を測定したいベンチマークテスト

代替手段

  • usingステートメントでIDisposableを正しく使う
  • GC.AddMemoryPressure() / GC.RemoveMemoryPressure()でGCにヒントを与える
  • メモリリークの根本原因を調査する(ツール:dotMemory, Visual Studio Profilerなど)