13

C# オブジェクト参照が実行時に (.NET CLR で) メモリ内でどのように表現されるか知りたいです。頭に浮かぶいくつかの質問は次のとおりです。

  1. オブジェクト参照はどのくらいのメモリを占有しますか? クラスのスコープとメソッドのスコープで定義された場合に違いはありますか? このスコープ (スタックとヒープ) に基づいて、それが存在する場所は異なりますか?

  2. オブジェクト参照内に維持される実際のデータは何ですか? それが参照するオブジェクトを指す単なるメモリアドレスですか、それともそれ以上のものがありますか? これは、クラスまたはメソッドのスコープ内で定義されているかどうかによって異なりますか?

  3. 上記と同じ質問ですが、今回はオブジェクト参照が参照によってメソッドに渡される場合のように、参照への参照について話します。1と2の答えはどう変わる?

4

2 に答える 2

14

.NET ヒープとスタック これは、スタックとヒープがどのように機能するかを徹底的に扱います。

C# およびその他の多くのヒープを使用する OOP 言語の一般的な参照語の使用このコンテキストでは、参照用のポインターではなくハンドルを使用します (C# はポインターを使用することもできます!)これ。このトピックに関する Eric Lippert の優れた投稿を参照してください。ハンドルはアドレスではありません

Handle がポインターのサイズであると言うのは適切ではありません。(たまたま同じかもしれませんが) ハンドルはオブジェクトのエイリアスであり、オブジェクトへの正式なアドレスである必要はありません。

この場合、CLR はたまたまハンドルに実際のアドレスを使用します: 上記のリンクから:

...CLR は、ガベージ コレクターが所有するオブジェクトへのアドレスとしてマネージド オブジェクト参照を実際に実装しますが、それは実装の詳細です。

したがって、ハンドルはおそらく 32 ビット アーキテクチャでは 4 バイト、64 バイト アーキテクチャでは 8 バイトですが、これは「確かに」ではなく、ポインタが直接の原因ではありません。コンパイラの実装と使用されるアドレス範囲によっては、ポインタの種類によってサイズが異なる場合があることに注意してください。

このすべてのコンテキストを使用して、おそらくポインタの類推によってこれをモデル化できますが、ハンドルがアドレスである必要はないことを理解することが重要です。CLR は、将来必要に応じてこれを変更することを選択でき、CLR の消費者はそれ以上のことを知る必要はありません。

この微妙なポイントの最終的なドライブ:

これは C# ポインターです。

int* myVariable;

これは C# ハンドルです。

object myVariable;

それらは同じではありません。

ハンドルでは行うべきではない、ポインターでの数学のようなことを行うことができます。ハンドルがたまたまポインターのように実装されていて、それをポインターのように使用している場合、ハンドルを何らかの方法で誤用しているため、後で問題が発生する可能性があります。

于 2012-02-29T02:21:21.357 に答える
13

この答えは、C/C++ ポインターを理解している場合に最も簡単に理解できます。ポインタは、単にデータのメモリアドレスです。

  1. オブジェクト参照はポインターのサイズである必要があり、通常、32 ビット CPU では 4 バイト、64 ビット CPU では 8 バイトです。どこで定義しても同じです。それがどこに住んでいるかは、それが定義されている場所によって異なります。それがクラスのフィールドである場合、それが属するオブジェクトのヒープに存在します。静的フィールドの場合、ガベージ コレクションの対象とならないヒープの特別なセクションに配置されます。ローカル変数の場合、スタック上に存在します。

  2. オブジェクト参照は単なるポインターであり、メモリ内のオブジェクトのアドレスを含む int または long として視覚化できます。どこで定義しても同じです。

  3. これは、ポインターへのポインターとして実装されます。データは同じです - メモリアドレスだけです。ただし、指定されたメモリ アドレスにはオブジェクトがありません。代わりに、オブジェクトへの元の参照である別のメモリ アドレスがあります。これにより、参照パラメータを変更できます。通常、パラメーターはメソッドが完了すると消えます。オブジェクトへの参照はパラメータではないため、この参照への変更は残ります。参照への参照は消えますが、参照は消えません。これは、参照パラメーターを渡す目的です。

知っておくべきことの1つは、値の型が所定の位置に格納されることです(メモリアドレスはなく、代わりにメモリアドレスがある場所に直接格納されます-#1を参照)。それらがメソッドに渡されると、コピーが作成され、そのコピーがメソッドで使用されます。それらが参照によって渡されると、メモリ内の値の型を特定するメモリ アドレスが渡され、値の型を変更できるようになります。

編集: dlev が指摘したように、これらの答えは厳格なルールではありません。.NET は、必要に応じてこれらの質問を自由に実装できます。ただし、これは Intel CPU が内部でどのように機能するかを実装する最も可能性の高い方法であるため、他の方法を使用すると効率が悪い可能性があります。

あまり混乱させていないことを願っていますが、説明が必要な場合はお気軽にお尋ねください。

于 2012-02-29T00:42:49.470 に答える