C#でメモリリークが発生してしまい、それのデバッグに時間がかかってしまいましたので備忘録を残しておきます。 まず、前提としてC#ではガベージコレクションによりメモリが自動的に開放されます。 ですが、それでもメモリが解放されなかったり直ぐにメモリを解放しておきたい場合があります。
今回はそのような場合に気を付けることを記載しておきます。
使用しているメモリ量を確認したいとき
以下のようにすると使用しているメモリ量をコンソールに表示することができます。これでメモリの増減を確認することができます。
1 |
Console.WriteLine(GC.GetTotalMemory(false)); |
配列のメモリを解放したいとき
配列のメモリを直ぐに開放したいときは以下のように行います。
VisualStudioでC#のプロジェクトを作成し、以下のソースを書き込んで実行してみます。
配列aを用意してメソッド内でそれをループさせています。メソッドからリターンされてきたタイミングではメモリが高くなりますので、配列にNULLを入れて”もう使用しない”ことを明示します。ただし、この時点ではまだメモリ解放されていません。 次の「GC.Collect()」を呼ぶことで使用しなくなったメモリを解放することができます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
using System; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Diagnostics; namespace memory { class Program { static void Main(string[] args) { Console.WriteLine(GC.GetTotalMemory(false)); // Low memory string[] a = new string[1000]; a = makeA(a); Console.WriteLine(GC.GetTotalMemory(false)); // High memory a = null; Console.WriteLine(GC.GetTotalMemory(false)); // High memory GC.Collect(); Console.WriteLine(GC.GetTotalMemory(false)); // Low memory } ~Program() { Console.WriteLine("Program died"); } static string[] makeA(string[] a) { for (int i = 0; i < 1000; i++) { a[i] = new String('A', 1000); } return a; } } } |
実行するとコンソールが立ち上がりメモリの増減を確認することができます。
ただし、メモリはReleaseモードでビルドしたときは解放されますが、Debugモードでビルドしたときは解放されないことがわかりました。 Debugモードでビルドした場合、以下のように「GC.Collect()」を呼んだあともメモリが減少していないことが分かります。
その他の注意点
・メモリリークが発生していたらクラスの中にDispose()が用意されていないか確認します。用意されたいたらそれを処理の最後に実行し、Disposeメソッドの中でメモリの解放が行われていることを確認しましょう。
・イベントを登録している場合、使わなくなったら削除していることを確認します。
・メモリが増加するのはインスタンスが生成(new)されたときです。生成された分だけインスタンスが削除されているかどうか確認します。