C#のメモリの解放の方法を解説!ガベージコレクタがあるのでメモリの解放は不要・手動で解放が必要なリソースもある

エンジニア
マネージャー
C#のメモリの解放とは?
C#のプログラミングをする上で、メモリの解放を考慮している人はどのくらい居るでしょうか。C言語のように古い時代からあるプログラミング言語と違って、C#はメモリについてはあまり気にする必要がないのは確かです。
とはいえ、C#でもメモリの解放を行う必要があるケースは存在します。この記事ではそれについて初心者に一から分かりやすく解説するので是非ご覧ください。
メモリを解放しないとメモリリークが生じる
プログラムで使用したメモリの解放を忘れてそのままになってしまうことを、メモリリークと言います。メモリリークを繰り返すと使用できるメモリが少なくなって、コンピュータの動作に支障が生じます。
そもそもメモリとは何なのでしょうか。メモリとはプログラムで使うリソース(資源)を保存する領域です。以下のようにフィールドを宣言したり、クラスをnewしてインスタンスを生成すると、メモリを消費します。
1 2 3 |
string str = "あいうえお"; // メモリを消費します DateTime dt = new DateTime(); // これもメモリを消費します |
ガベージコレクタがあるのでメモリの解放は不要
しかしC#でプログラミングをしていて、フィールドの宣言やnewをした後、それで使用したメモリを解放をしている人はいないでしょう。C言語のような古い言語はメモリリークを防ぐためにメモリの解放が必要でしたが、C#はその必要はありません。なぜでしょうか?
それはC#にはガベージコレクタという親切な機能があって、メモリリークを防いでくれるからです。ではガベージコレクタとはどんな機能なのでしょうか?
ガベージコレクタの役割
C#のガベージコレクタとは、使用しなくなったリソースを自動的に開放してくれる機能です。不要になった瞬間に開放されることはないですが、しばらくしてから解放してくれます。手動で解放するよりも遅れますが、解放忘れが無くなるのでガベージコレクタの方が安全です。
なおC#では「GC.Collect()」を呼べばガベージコレクタを手動で実行することもできます。ただしこれを呼ばなくてもガベージコレクタは自動で実行されるので、特に意識して使う必要はありません。
手動で解放が必要なリソースもある
ではC#ではメモリリークの心配は全くないのでしょうか。実はそうではありません。C#のリソースは「マネージ」と「アンマネージ」の二種類があります。
マネージのリソースはここまで述べてきたフィールドやクラスのインスタンスが該当し、ガベージコレクタにより自動でメモリ解放されます。
しかしアンマネージのリソースはガベージコレクタで解放されません。アンマネージに該当するリソースは、ファイルの読み書きやネットワークの送受信処理などのハードウェア寄りの機能で使われるリソースが該当します。
手動で解放する例
手動でリソースの解放を行う具体例を見てみましょう。以下はファイルを開いて中身をコンソールに表示します。
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 |
using System; using System.IO; class Program { static void Main(string[] args) { StreamReader sr = null; try { sr = new StreamReader("c:\\test\\test.txt"); string str = sr.ReadToEnd(); Console.WriteLine(str); } catch (FileNotFoundException e) { Console.WriteLine("ファイルがありませんでした。"); } finally { if (sr != null) { sr.Close(); } } } } |
finallyで解放漏れを防げる
上のサンプルではファイルを開いて文字列として全て読み込んで表示しています。例外が発生した場合はファイルが無いというメッセージを表示します。最後、finally節でファイルをクローズしています。この処理がアンマネージのリソースの解放です。
なおfinally節は必ず最後に実行されます。もし例外が発生しても実行されます。そのためfinally節にファイルをクローズする処理を実行すれば、解放忘れが無いので安心です。
usingステートメントの使い方
なお、上のファイル操作処理は以下のように書くこともできます。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
try { using (StreamReader sr = new StreamReader("c:\\test\\test.txt")) { string str = sr.ReadToEnd(); Console.WriteLine(str); } } catch (FileNotFoundException e) { Console.WriteLine("ファイルがありませんでした。"); } |
usingステートメントでコードを簡潔に
このサンプルで使用しているStreamReaderクラスの親クラスのTextReaderは、IDisposableインターフェイスを実装しています。そのようなクラスを使う場合は、上のようにusingステートメントを使うことで、finally節でのクローズ処理を省略することができるのです。
usingを使えば簡潔で間違いのないコードを記述することができます。是非活用しましょう。
ファイナライザーについて
C#にはファイナライザーという機能があります。使い方の例は以下の通りです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
using System; class Program { class Test { ~Test() { Console.Write("ファイナライザーが呼ばれました。"); } } static void Main(string[] args) { TestWork(); GC.Collect(); } static void TestWork() { Test t = new Test(); } } |
ファイナライザーはデストラクタとも言われる
上のC#サンプルの~Test()というメソッドがファイナライザーです。このメソッドはデストラクタとも言われます。名前の通り、コンストラクタと対になる機能です。
コンストラクタはクラスのインスタンスが生成された時に呼ばれます。それとは逆に、ファイナライザーはクラスのインスタンスが解放された時に呼ばれるのです。
ガベージコレクタによりファイナライザーが呼ばれる
このサンプルではMainメソッドから呼ばれるTestWorkメソッドでTestクラスを生成します。その後にMainに戻った後は、このインスタンスは不要になります。そこでGC.Collct()を実行するとガベージコレクタが動作して、不要になったインスタンスのメモリを解放します。
この時にファイナライザーが呼ばれるのです。このサンプルの実行結果は以下のように表示されます。
ファイナライザーが呼ばれました。
ファイナライザーの別のケース
上のサンプルを少し変更します。フィールドのtをProgramクラスのstaticなのメンバにします。
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 |
using System; class Program { static Test t = null; class Test { ~Test() { Console.Write("ファイナライザーが呼ばれました。"); } } static void Main(string[] args) { TestWork(); t = null; GC.Collect(); } static void TestWork() { t = new Test(); } } |
null代入でガベージコレクタの対象にする
今回のサンプルではtがstaticなメンバ変数のため、TestWorkメソッドを抜けてもtのインスタンスは不要になりません。そこでtにnullを代入して強制的に不要扱いにしています。
これにより実行すると前回同様「ファイナライザーが呼ばれました。」が表示されます。ガベージコレクタにより解放されていることがわかります。
試しにMainメソッドのt = null;を削除すると、ファイナライザーが実行されません。Testクラスのインスタンスが残ったままになっています。これがメモリリークです。
このnullを代入するというテクニックは、無駄なインスタンスをガベージコレクトの対象にさせるために有効です。
エンジニア
マネージャー
C#のメモリの解放は意識する必要はある
C#のメモリの解放について解説しましたがご理解頂けましたでしょうか。C#はガベージコレクタがあるのであまりメモリを意識する必要はないですが、アンマネージリソースを解放したり、無駄なインスタンスはnullを代入するといったことは考慮する必要があります。
FEnet.NETナビ・.NETコラムは株式会社オープンアップシステムが運営しています。
株式会社オープンアップシステムはこんな会社です
秋葉原オフィスには株式会社オープンアップシステムをはじめグループのIT企業が集結!
数多くのエンジニアが集まります。

-
スマホアプリから業務系システムまで
スマホアプリから業務系システムまで開発案件多数。システムエンジニア・プログラマーとしての多彩なキャリアパスがあります。
-
充実した研修制度
毎年、IT技術のトレンドや社員の要望に合わせて、カリキュラムを刷新し展開しています。社内講師の丁寧なサポートを受けながら、自分のペースで学ぶことができます。
-
資格取得を応援
スキルアップしたい社員を応援するために資格取得一時金制度を設けています。受験料(実費)と合わせて資格レベルに合わせた最大10万円の一時金も支給しています。
-
東証プライム上場企業グループ
オープンアップシステムは東証プライム上場「株式会社オープンアップグループ」のグループ企業です。
安定した経営基盤とグループ間のスムーズな連携でコロナ禍でも安定した雇用を実現させています。
株式会社オープンアップシステムに興味を持った方へ
株式会社オープンアップシステムでは、開発系エンジニア・プログラマを募集しています。
年収をアップしたい!スキルアップしたい!大手の上流案件にチャレンジしたい!
まずは話だけでも聞いてみたい場合もOK。お気軽にご登録ください。


C#新着案件New Job
システム開発/東京都新宿区/【WEB面談可/C#経験者/20代前半の方活躍中/経験1年以上の方活躍中】/在宅勤務
月給29万~34万円東京都新宿区(新宿駅)システム開発/東京都新宿区/【WEB面談可/C#経験者/20代後半~40代の方活躍中/経験年数不問】/在宅勤務
月給41万~50万円東京都新宿区(新宿駅)デバック、テスト項目の作成/神奈川県横浜市/【WEB面談可/C#経験者/20代前半の方活躍中/経験1年以上の方活躍中】/在宅勤務
月給29万~34万円神奈川県横浜市(桜木町駅)デバック、テスト項目の作成/神奈川県横浜市/【WEB面談可/C#経験者/20代後半~40代の方活躍中/経験年数不問】/在宅勤務
月給41万~50万円神奈川県横浜市(桜木町駅)基幹システム開発導入/東京都新宿区/【WEB面談可/C#経験者/20代前半の方活躍中/経験1年以上の方活躍中】/在宅勤務
月給29万~34万円東京都新宿区(西新宿駅)基幹システム開発導入/東京都新宿区/【WEB面談可/C#経験者/20代後半~40代の方活躍中/経験年数不問】/在宅勤務
月給41万~50万円東京都新宿区(西新宿駅)