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

公開日時:   更新日時:
C#のメモリの解放の方法を解説!ガベージコレクタがあるのでメモリの解放は不要・手動で解放が必要なリソースもある
基本情報技術者試験の試験対策はこちら>>

SE
C#のメモリの解放の方法について教えてください。

PM
C#のメモリの解放が必要なケースなどについて説明しましょう。

C#のメモリの解放とは?


C#のプログラミングをする上で、メモリの解放を考慮している人はどのくらい居るでしょうか。C言語のように古い時代からあるプログラミング言語と違って、C#はメモリについてはあまり気にする必要がないのは確かです。

とはいえ、C#でもメモリの解放を行う必要があるケースは存在します。この記事ではそれについて初心者に一から分かりやすく解説するので是非ご覧ください。

メモリを解放しないとメモリリークが生じる

プログラムで使用したメモリの解放を忘れてそのままになってしまうことを、メモリリークと言います。メモリリークを繰り返すと使用できるメモリが少なくなって、コンピュータの動作に支障が生じます。

そもそもメモリとは何なのでしょうか。メモリとはプログラムで使うリソース(資源)を保存する領域です。以下のようにフィールドを宣言したり、クラスをnewしてインスタンスを生成すると、メモリを消費します。

ガベージコレクタがあるのでメモリの解放は不要

しかしC#でプログラミングをしていて、フィールドの宣言やnewをした後、それで使用したメモリを解放をしている人はいないでしょう。C言語のような古い言語はメモリリークを防ぐためにメモリの解放が必要でしたが、C#はその必要はありません。なぜでしょうか?

それはC#にはガベージコレクタという親切な機能があって、メモリリークを防いでくれるからです。ではガベージコレクタとはどんな機能なのでしょうか?

ガベージコレクタの役割

C#のガベージコレクタとは、使用しなくなったリソースを自動的に開放してくれる機能です。不要になった瞬間に開放されることはないですが、しばらくしてから解放してくれます。手動で解放するよりも遅れますが、解放忘れが無くなるのでガベージコレクタの方が安全です。

なおC#では「GC.Collect()」を呼べばガベージコレクタを手動で実行することもできます。ただしこれを呼ばなくてもガベージコレクタは自動で実行されるので、特に意識して使う必要はありません。

手動で解放が必要なリソースもある

ではC#ではメモリリークの心配は全くないのでしょうか。実はそうではありません。C#のリソースは「マネージ」と「アンマネージ」の二種類があります。

マネージのリソースはここまで述べてきたフィールドやクラスのインスタンスが該当し、ガベージコレクタにより自動でメモリ解放されます。

しかしアンマネージのリソースはガベージコレクタで解放されません。アンマネージに該当するリソースは、ファイルの読み書きやネットワークの送受信処理などのハードウェア寄りの機能で使われるリソースが該当します。

手動で解放する例

手動でリソースの解放を行う具体例を見てみましょう。以下はファイルを開いて中身をコンソールに表示します。

finallyで解放漏れを防げる

上のサンプルではファイルを開いて文字列として全て読み込んで表示しています。例外が発生した場合はファイルが無いというメッセージを表示します。最後、finally節でファイルをクローズしています。この処理がアンマネージのリソースの解放です。

なおfinally節は必ず最後に実行されます。もし例外が発生しても実行されます。そのためfinally節にファイルをクローズする処理を実行すれば、解放忘れが無いので安心です。

usingステートメントの使い方

なお、上のファイル操作処理は以下のように書くこともできます。

usingステートメントでコードを簡潔に

このサンプルで使用しているStreamReaderクラスの親クラスのTextReaderは、IDisposableインターフェイスを実装しています。そのようなクラスを使う場合は、上のようにusingステートメントを使うことで、finally節でのクローズ処理を省略することができるのです。

usingを使えば簡潔で間違いのないコードを記述することができます。是非活用しましょう。

ファイナライザーについて

C#にはファイナライザーという機能があります。使い方の例は以下の通りです。

ファイナライザーはデストラクタとも言われる

上のC#サンプルの~Test()というメソッドがファイナライザーです。このメソッドはデストラクタとも言われます。名前の通り、コンストラクタと対になる機能です。

コンストラクタはクラスのインスタンスが生成された時に呼ばれます。それとは逆に、ファイナライザーはクラスのインスタンスが解放された時に呼ばれるのです。

ガベージコレクタによりファイナライザーが呼ばれる

このサンプルではMainメソッドから呼ばれるTestWorkメソッドでTestクラスを生成します。その後にMainに戻った後は、このインスタンスは不要になります。そこでGC.Collct()を実行するとガベージコレクタが動作して、不要になったインスタンスのメモリを解放します。

この時にファイナライザーが呼ばれるのです。このサンプルの実行結果は以下のように表示されます。

ファイナライザーが呼ばれました。

ファイナライザーの別のケース

上のサンプルを少し変更します。フィールドのtをProgramクラスのstaticなのメンバにします。

null代入でガベージコレクタの対象にする

今回のサンプルではtがstaticなメンバ変数のため、TestWorkメソッドを抜けてもtのインスタンスは不要になりません。そこでtにnullを代入して強制的に不要扱いにしています。

これにより実行すると前回同様「ファイナライザーが呼ばれました。」が表示されます。ガベージコレクタにより解放されていることがわかります。

試しにMainメソッドのt = null;を削除すると、ファイナライザーが実行されません。Testクラスのインスタンスが残ったままになっています。これがメモリリークです。

このnullを代入するというテクニックは、無駄なインスタンスをガベージコレクトの対象にさせるために有効です。

SE
C#のメモリの解放について理解できました。

PM
メモリーの解放を意識することで、メモリリークを解消しましょう。

C#のメモリの解放は意識する必要はある

C#のメモリの解放について解説しましたがご理解頂けましたでしょうか。C#はガベージコレクタがあるのであまりメモリを意識する必要はないですが、アンマネージリソースを解放したり、無駄なインスタンスはnullを代入するといったことは考慮する必要があります。


FEnetへの登録は左下のチャットが便利です 経験者優遇! 最短10秒!

.NET分野でのキャリアアップをお考えの方は、現在募集中の求人情報をご覧ください。

また、直接のエントリーも受け付けております。

エントリー(応募フォーム)

Search

Popular

reccomended

Categories

Tags