【C#】C#にメモリの解放は必要ない?usingやDisposeを使う理由について。使い方サンプルつき。

目次

はじめに

C言語を使用しているかたは、メモリの解放を意識してコーディングされていることかと思います。
解放されないメモリが溜まると、メモリ使用量が増えていく現象(メモリリーク)が発生するからです。
C#では、メモリ(.NET Frameworkが管理しているメモリ)についてあまり意識する必要はありません。
それは、ガベージコレクション(GC)という仕組みによりメモリが自動的に開放されるからです。
ですが、このガベージコレクションによるメモリ解放の対象外が存在します。
詳しくみていきましょう。

ガベージコレクション(GC)とは

.NET Frameworkには自動でメモリを解放してくれるガベージコレクションが搭載されています。
ガベージコレクションは、プログラムが使用し終わったメモリ領域を自動的に回収して,ほかのプログラムが利用できるようにするための技術です。

ガベージコレクションが起こるタイミングは?

  • システムの物理メモリが少ない場合
  • マネージド ヒープで割り当てられたオブジェクトによって使用されているメモリが、許容される閾値を超えた場合
  • GC.Collect メソッドが呼び出された場合

などらしいです。

ガベージコレクションの対象外のリソースが存在する


ガベージコレクションによるメモリ解放の対象外が存在します。
ファイルやデータベース、Excel、ネットワークなどの外部リソースを使用する .NET Framework のクラス、また、static変数(静的メンバー)を利用する場合です。
メモリ解放をしない場合、例えばファイルをオープンしっぱなしで他から利用できなくなったり、メモリリーク(※)が発生してしまうので、これらを利用する場合は、明示して解放する必要があります。

(※)メモリリークとは、プログラミングにおけるバグの1つで、実行中のプログラムがメモリ領域の解放を行わないまま放置してしまうことなどが原因で発生します。

usingステートメントを使ってメモリの解放を明示する

usingステートメントを利用すると、ブロック{}を抜けた際に「Dispose()」を自動的に呼び出してくれます。また、returnをした場合、例外がスローされた場合でも、確実にメモリを解放してくれます。
お作法的な書き方があるので、後述するパターンを覚えておきましょう。
なお、以下のようにtry{…}finally{…}で、finally句で「Dispose()」を呼び出すことで確実にメモリ解放はできますが、usingを使ったほうがスマートであり、なおかつ、メモリの解放をし忘れることを防ぐために、使うべきかと思います。

try-catch-finallyを用いてメモリを解放する例

FileStream fs = null;
try
{
    // ファイルを開く
    fs = new FileStream(@"C:\Work\test.txt", FileMode.Open);
}
catch (Exception ex)
{
    Console.WriteLine(ex.Message);
}
finally
{
    // 破棄
  // 成功、失敗に関わらず必ず実行される
    fs.Dispose();
}

1.ファイルをOPEN、読込みする(FileStream,StreamReader)

usingは複数行並べたり、ネストすることができます。

FileStream fs = null;
try
{
    using (FileStream fs = new FileStream(@"C:\Work\test.txt", FileMode.Open))
   using (StreamReader reader = new StreamReader(fs)
    {
       // 処理
    }
}
catch (Exception ex)
{
     Console.WriteLine(ex.Message);
}

2.データベースに接続、コマンドを実行する(DBクラス)

using (SqlConnection connection = new SqlConnection())
{
   // 接続を確立
   connection.Open();
 
   using (SqlCommand command = new SqlCommand("select * from SyainMst", connection))
   {
    // SQL実行
     using (SqlDataReader reader = command.ExecuteReader())
     {
          // 実行結果を読込
          while (reader.Read())
          {
             Console.WriteLine(String.Format("{0}, {1}", reader[0], reader[1]));
          }
       }
   }
}

今日はここまで。