.netマネージメモリはオブジェクト内の値型をどのように処理しますか?
質問
public class MyClass
{
public int Age;
public int ID;
}
public void MyMethod()
{
MyClass m = new MyClass();
int newID;
}
私の理解では、次のことが当てはまります。
- 参照mはスタック上に存在し、MyMethod()が終了すると範囲外になります。
- 値型newIDはスタックに存在し、MyMethod()が終了するとスコープ外になります。
- new演算子によって作成されたオブジェクトはヒープ内に存在し、MyMethod()が終了すると、オブジェクトへの他の参照が存在しないと仮定すると、GCによって回収可能になります。
ここに私の質問があります:
- オブジェクト内の値型はスタックまたはヒープに存在しますか?
- オブジェクトのボックス化/ボックス化解除の値は問題ですか?
- このトピックに関する詳細かつ理解しやすいリソースはありますか?
論理的には、クラス内の値型はヒープ内にあると思いますが、そこに到達するためにボックス化する必要があるかどうかはわかりません。
編集:
このトピックの推奨読書:
解決
クラスの値型の値は、マネージヒープ内のオブジェクトインスタンスと共存する 。メソッドのスレッドのスタックは、メソッドの期間中のみ有効です。そのスタック内にのみ存在する場合、どのように値を保持できますか?
マネージヒープ内のクラスのオブジェクトサイズは、値型フィールド、参照型ポインター、およびSyncブロックインデックスなどの追加のCLRオーバーヘッド変数の合計です。オブジェクトの値型フィールドに値を割り当てると、CLRはその特定のフィールドのオブジェクト内に割り当てられたスペースに値をコピーします。
たとえば、単一のフィールドを持つ単純なクラスを取り上げます。
public class EmbeddedValues
{
public int NumberField;
}
そしてそれで、簡単なテストクラス。
public class EmbeddedTest
{
public void TestEmbeddedValues()
{
EmbeddedValues valueContainer = new EmbeddedValues();
valueContainer.NumberField = 20;
int publicField = valueContainer.NumberField;
}
}
.NET Framework SDKが提供するMSIL逆アセンブラーを使用して、EmbeddedTest.TestEmbeddedValues()のILコードを覗く場合
.method public hidebysig instance void TestEmbeddedValues() cil managed
{
// Code size 23 (0x17)
.maxstack 2
.locals init ([0] class soapextensions.EmbeddedValues valueContainer,
[1] int32 publicField)
IL_0000: nop
IL_0001: newobj instance void soapextensions.EmbeddedValues::.ctor()
IL_0006: stloc.0
IL_0007: ldloc.0
IL_0008: ldc.i4.s 20
IL_000a: stfld int32 soapextensions.EmbeddedValues::NumberField
IL_000f: ldloc.0
IL_0010: ldfld int32 soapextensions.EmbeddedValues::NumberField
IL_0015: stloc.1
IL_0016: ret
} // end of method EmbeddedTest::TestEmbeddedValues
CLRに、「20」のロード値を stfld するように指示されていることに注意してください。スタック内で、読み込まれたEmbeddValuesのNumberFieldフィールドの場所に、管理されたヒープに直接。同様に、値を取得する場合、 ldfld 命令を使用して、その管理ヒープの場所からスレッドスタックに値を直接コピーします。これらのタイプの操作では、ボックス化/アンボックス化は行われません。
他のヒント
- オブジェクトが所有するすべての参照または値型はヒープ内に存在します。
- intをオブジェクトにキャストする場合のみ。
これに関して私が見た中で最高のリソースは、Jeffrey RichterによるC#経由のCLRの本です。 .NET開発を行う場合は、読む価値があります。そのテキストに基づいて、私の理解では、参照型内の値型は親オブジェクトに埋め込まれたヒープに存在するということです。参照タイプは、ヒープ上では常に常にです。 ボクシングとアンボクシングは対称ではありません。ボクシングは、ボクシング解除よりも大きな懸念事項です。ボクシング では、値型の内容をスタックからヒープにコピーする必要があります。これが発生する頻度によっては、クラスの代わりに構造体を使用しても意味がない場合があります。パフォーマンスに重要なコードがあり、ボックス化とボックス化解除が行われているかどうかわからない場合は、ツールを使用してメソッドのILコードを調べます。 ILに「box」と「unbox」という単語が表示されます。個人的には、コードのパフォーマンスを測定してから、これが心配の対象かどうかを確認します。あなたの場合、これはそれほど重大な問題になるとは思いません。参照型内でこの値型にアクセスするたびに、スタックからヒープ(ボックス)にコピーする必要はありません。そのシナリオでは、ボクシングがより意味のある問題になります。
- Ans#1:ヒープ。優れた「Essential .Net Vol 1」からドンボックスを言い換えます
参照タイプ(RT)は、常にヒープに割り当てられたインスタンスを生成します。 対照的に、値タイプ(VT)はコンテキストに依存します-ローカルvarがVTである場合、CLRはスタックにメモリを割り当てます。クラスのフィールドがVTのメンバーである場合、CLRは、フィールドが宣言されているオブジェクト/タイプのレイアウトの一部としてインスタンスのメモリを割り当てます。
-
Ans#2:いいえ。ボクシングは、オブジェクト参照/インターフェイスポインターを介して構造体にアクセスする場合にのみ発生します。 obInstance.VT_typedfield はボックス化されません。
RT変数には、参照するオブジェクトのアドレスが含まれます。 2 RT変数は同じオブジェクトを指すことができます。 対照的に、VT変数はインスタンスそのものです。 2 VT変数は同じオブジェクト(構造体)を指すことはできません
-
Ans#3:Don Box's Essential .net / Jeffrey RichterのCLR経由のCLR。私は前者のコピーを持っています...しかし、後者は.Netリビジョンのためにさらに更新されるかもしれません
オブジェクト内の値型はスタックまたはヒープに存在しますか?
ヒープ上。これらは、参照を保持するポインターがそうであるように、オブジェクトのフットプリントの割り当ての一部です。
オブジェクトのボックス化/ボックス化解除の値の種類は問題ですか?
ここにはボクシングはありません。
このトピックに関する詳細かつ理解可能なリソースはありますか?
リヒターの本に対する+1票。
構造型の変数またはその他の保存場所は、その型のパブリックおよびプライベートインスタンスフィールドの集合です。与えられた
struct Foo {public int x,y; int z;}
宣言 Foo bar;
は、 bar.x
、 bar.y
、および bar.z
を引き起こします。 bar
が保存される場所に保存されます。このような bar
の宣言をクラスに追加することは、ストレージレイアウトの観点から、3つの int
フィールドを追加することと同等です。実際、フィールドにアクセスする以外に bar
で何もしなかった場合、 bar
のフィールドは3つのフィールド bar_x
、< code> bar_y 、および bar_cantaccessthis_z
[最後のものにアクセスするには、フィールドにアクセスする以外に bar
を使用する必要がありますが、実際にあらゆる用途に使用されています]。
構造型の保存場所をフィールドの集合体として認識することは、構造を理解するための最初のステップです。ある種のオブジェクトを保持していると見なそうとすると、「簡単」に見えるかもしれませんが、実際の動作とは一致しません。