C#を勉強しなおしてみた(8)
スタックとヒープ

どもです。

本投稿は、以下の投稿の続きモノです。

今回は、「スタック」と「ヒープ」について書きます。

1. スタックとヒープとは

スタックとヒープは、それぞれ「ヒープメモリ」や「ヒープ領域」、「スタックメモリ」や「スタック領域」と呼ばれる「メモリ領域」であり、プログラムが使用するメモリ領域です。これらのメモリは、以下のように使用されます。

  • スタックメモリ:自動変数(ローカル変数)を保存する
  • ヒープメモリ:動的な変数を保存する

C#で実装されたプログラムが、これらの領域をどのように使用するか見てみます。

1.1. ヒープメモリの使い方

まず、ヒープメモリについてです。C#のプログラムでは、「参照型」がヒープメモリに割り当てます。そのため、ガベージコレクションの対象となります。(ガベージコレクションの詳細は、MSDNのページを参照してください。)

過去の記事でも書いているのですが、参照型の変数の代入では「参照」がコピーされます。そのため、「参照型変数の代入」処理を行っても、ヒープメモリ上に確保したオブジェクトはコピーされません。(ヒープ上メモリ上への参照、要は「アドレス」的なモノがコピーされます。)

1.2. スタックメモリの使い方

次に、スタックメモリです。C#のプログラムでは、「値型」がスタックメモリに割り当てられます。スタックメモリ上に割り当てられた「値型」は、ガベージコレクションの対象になりません。スタックメモリ上の「値型」は、その変数を宣言した処理(多くの場合「メソッド」「関数」)が終了することで、解放されます。

2. スタックとヒープのイメージ

文字でスタックとヒープの内容を説明しても理解しにくい部分があると思います。なので、図を交えて解説をしてみます。(ここで参照する図は、過去の記事の図を修正して載せます。)

まずヒープメモリには、「参照型」のオブジェクトが割り当てられます。これを具体的に考えるために、以下のようなクラスのインスタンスの割り当てを考えます。

public class Worker
{
	public int ID { get; set; }
	public string Name { get; set; }
}

このクラスのオブジェクトを、以下のコードでインスタンス化します。

Worker workerA = new Worker(){
	ID = 1,
	Name "hogehoge"
};

このコードが実行されると、ヒープメモリ上には、以下のようなオブジェクトが配置されます。



ヒープメモリ上に配置されたオブジェクトを参照するための情報(変数「workerA」)は、スタックメモリ上に配置されます。変数「workerA」には、スタックメモリ上のアドレス情報が格納されます。

次に、以下のように「workerB」を宣言し、「workerA」を代入します。

Worker workerB = workerA;

このとき、Workerはクラスであり参照型なので、このコードを実行することで、ヒープメモリ上の変数「workerB」には、変数「workerA」が指すWorkerオブジェクトのアドレスが格納されます。



次に、以下のように値型の変数を宣言、初期化してみます。

int intValA = 10;
	int intValB = 20;

このコードが実行されると、下図のようにヒープメモリに値が格納されます。



値型の場合には、スタックメモリに新規に領域が確保されることはありません。

また、以下のように新規に変数を宣言し、既存の値を代入します。

int intValC = intValB;

その結果、変数intValCには、intValBと同じ値が格納されます。



このとき、intValBとintValCは、別の領域に確保され、別の情報として扱われます。

3. まとめ

今回は、C#におけるスタックとヒープについて書きました。

スタックとヒープは、ともにメモリ領域です。これらの領域は、格納する情報が異なります。スタックには、参照型のオブジェクトが格納され、ヒープには値型の情報が格納されます。また、参照型のオブジェクトの参照情報も、ヒープに格納されます。スタックは、

また、これらの領域では、「確保」されている情報が「解放」されるタイミングが異なります。スタック領域に確保された情報は、その情報が参照されなくなったタイミングで、ガベージコレクションによって解放されます。それに対してヒープは、領域を宣言(確保)した処理が完了した場合に、自動で解放されます。

このように、スタックとヒープは、それぞれ格納される情報や解放されるタイミングが異なります。そのため、どのような情報がスタックに格納され、どのような情報がヒープに格納されるのかは、できれば設計、少なくとも実装時に注意しておく必要があるかと思います。そうすることで、メモリリークが発生したり、メモリ使用量が大きくなってしまうようなプログラムを作成してしまうことが無くなるのでは無いか、と思います。

この記事が誰かの助けになれば幸いです。

ではっ!