C#のメモリ領域

2023年1月26日

メモリ管理の概要について考えてみましょう

メモリ領域の種類

実際の仕組みは非常に複雑になっています
用語もたくさんあり、概略、かつ簡易にまとめるためにマイクロソフトの内部ドキュメントと一致しないところもありますので、学習用の参考としてください

スタック領域

スタック領域は、メソッド呼び出しに関連して使用されるメモリ領域で、呼び出し元のメソッドに戻るために必要な情報(例えば、呼び出し元のメソッドが実行していたプログラムカウンタ)を保存します。スタック領域は、LIFO(Last In First Out)というデータ構造に基づいて管理され、スタックのトップにあるデータが最後に取り出されます。スタック領域は、メソッドが呼び出されるたびに確保され、メソッドから返るときに解放されます。スタックは、高速なアクセスが可能であり、データのサイズが小さいため、ここにデータを格納することができます。また、ローカル変数やメソッドの引数もここに置かれます

ヒープ領域

 コード

プログラムのインストラクションが格納されるメモリ領域で、プログラムが実行されるために必要な命令が保持され、CPUがアクセスして実行することができます

静的データ

スタティックフィールドや、スタティックメソッドなどが格納されます。アプリケーションが終了するまでメモリが保持されます。インスタンスデータとは違い、クラスごとのデータになります
格納場所は、メソッドテーブルと呼ばれる領域の最後になります
各クラスとインターフェイスは、AppDomainにロードされると、メソッドテーブルデータ構造によってメモリに表現されます。これは、オブジェクトの最初のインスタンスが作成される前のクラスロードアクティビティの結果です。オブジェクト(インスタンス)は状態を表しますが、メソッドテーブルは動作を表します

インスタンスデータ

ヒープ領域は、プログラムが動的にメモリを確保するために使用される領域で、長期間にわたってデータを保持する必要がある場合に使用されます。例えば、クラスのインスタンス(new 演算子の実行)、配列、文字列などの参照型の変数の実体がここに格納されます。ヒープ領域は、プログラムが実行される間中、使用され続けます

クラスと構造体、列挙型のメモリ配置について

構造体

C#では、構造体は値型であり、インスタンスを作成すると、そのインスタンスに対応する領域がスタック上に確保されます。

構造体は、値型の変数として宣言され、インスタンスを作成する際に、そのインスタンスに対応する領域がスタック上に確保されます。インスタンスがスコープを抜けると、そのインスタンスに対応する領域は自動的に解放されます。

構造体は、参照型のクラスと比較し、小さなデータを扱うのに適しています。構造体には、デフォルトのコンストラクタがあり、インスタンスを作成する際に、デフォルト値で初期化されます。

ただし、構造体は参照型のクラスと異なり、メソッドを持つことはできません。また、継承やポリモーフィズムもサポートしていません。

結論として、C#では構造体は値型であり、インスタンスを作成する際に、そのインスタンスに対応する領域がスタック上に確保されます。

クラス

C#では、クラスはインスタンスを作成することで、ヒープ領域に格納されます。クラス自体は、静的メモリ領域に格納されます。

クラスは、インスタンスを作成するためのテンプレートであり、インスタンスは、そのクラスから作成された実体です。そのため、クラス自体は、静的に定義されたデータやメソッドを持ち、インスタンスは、そのクラスから作成された実体であり、インスタンスには、インスタンス変数が格納されます。

インスタンス変数は、プログラムが実行中に確保され、不要になった場合に解放される領域であり、それがヒープ領域に格納されます

列挙型

C#で宣言された列挙型(enum)は、スタック領域に格納されます。

列挙型は整数型のシンタックスシュガーで、実質的には整数型の変数と同じように扱われます。
そのため、列挙型変数は、スタックに保存され、関数の引数やローカル変数として扱われます。

ただし、列挙型は値型なので、値がコピーされることがあり、大きなデータを保持する場合はヒープ上に配置される参照型のオブジェクトを使用することをおすすめします。

また、列挙型は値型のため、列挙型変数は、値のコピーを渡すことができます。

スタティックフィールドとインスタンスフィールド
(スタティックプロパティ、インスタンスプロパティ)

class Player
{
    public static int count = 3;
    public int score = 5;
}

メモリ管理の違い

スタティックフィールド

C#でのスタティックフィールドは、.NETのランタイムによって管理されるヒープ領域の静的データを保存するエリアに置かれます。

インスタンスフィールド

一方、インスタンスフィールドは、インスタンスが作成されると、そのインスタンスに対応する領域がヒープ上に確保され、そのインスタンスが破棄されると解放されます。

スタティックフィールドがメモリに展開されるタイミング

C#でスタティックフィールドは、そのクラスが最初に使用されるタイミングで確保されます。これは、C#のランタイムによって「クラスの最初の使用」として定義されています。「クラスの最初の使用」は、そのクラスが以下のいずれかの方法で初めて参照される時点を指します。

  • スタティックメンバーが呼び出される
  • インスタンスが作成される
  • 静的コンストラクタが呼び出される

これにより、スタティックフィールドは、プログラム起動時には確保されなくても、クラスが最初に使用されるタイミングで確保されます。

ただし、スタティックフィールドは、プログラム終了まで解放されず、次回のプログラム起動時もその値が使用される。

また、スタティックフィールドは、プログラムのライフタイム中、常に存在し、アクセス可能です。

参考

詳細資料

上記の記載は、この資料に基づいています
ただし、原本は非常に難易度が高いため、なるべく簡易に理解できるようにしています
ですから、資料と一致していないところもありますので、大枠で捉えてください
詳細レベルで把握したい場合、原本を拝読することをお勧めします

この記事では、以下について説明されています
• SystemDomain、SharedDomain、DefaultDomain
• オブジェクトレイアウトおよびその他のメモリの詳細
• メソッドテーブルレイアウト
• メソッドディスパッチ

イラストでまとめたもの

初学者の学習用に作成したものです
現実の実装とは一部異なることがありますので、ご了承ください

C#,学習

Posted by hidepon