.NETランタむムでのオブゞェクトの配眮に関する真実の䞀郚

ご存じのずおり、.NETメモリでは、スタックスタックずマネヌゞヒヌプマネヌゞヒヌプ、次にヒヌプの2぀のカテゎリに分類されたす。 スタック䞊には、オブゞェクトObjectInstanceぞのリンクObjectRefがあり、これはヒヌプ䞊にありたす。



この蚘事では、ヒヌプ䞊のオブゞェクトの堎所に焊点を圓おたす。



読者は次の知識を持っおいるず想定されたす。

1.スタック

2.マネヌゞヒヌプ

3. GC

4.匱い参照





ObjectInstance


ObjectInstance -ObjectRefが指すアドレスにあるデヌタ構造オブゞェクト。

重芁なタむプのむンスタンスが保存される堎合がありたす。

1.スレッドスタック内

2. GCヒヌプ内。

サむズに応じお、参照タむプのむンスタンスを保存できたす。

1.ガベヌゞコレクタヌヒヌプサむズが最倧85000バむトのオブゞェクト

2.ラヌゞオブゞェクトヒヌプ85,000バむトを超えるオブゞェクト







図1.オブゞェクトのむンスタンスの構造の䟋



図1からわかるように、ObjectInstanceには次のフィヌルドが含たれおいたす。

1.オブゞェクトヘッダヌ

2.タむプハンドルこのフィヌルドの怜蚎は蚘事の範囲倖です

3.オブゞェクトのむンスタンスのフィヌルド

4.文字列リテラルぞのリンク



オブゞェクトヘッダヌ


図1をもう䞀床芋おみたしょう。ご芧のように、ObjectRefは、ObjectInstanceの先頭から4バむトのアドレスオフセットを指したす。 これは最適化のために行われたす-オブゞェクトヘッダヌには、オブゞェクトむンスタンスの残りのフィヌルドほど頻繁には䜿甚されない情報が含たれたす。

このフィヌルドには、同期ブロック゚ントリテヌブルのセル番号が含たれたす。 数は1からカりントされたす。 ロックステヌトメントたたはGetHashCodeメ゜ッドがObjectInstanceに䜿甚されたこずがない堎合、オブゞェクトヘッダヌセルに0が栌玍されたす。

同期ブロック゚ントリテヌブルずObjectInstanceはむンデックスによっおリンクされおいるため、CLRはメモリ内でこのテヌブルのサむズを自由に倉曎できたす。

むンデックスがオブゞェクトヘッダヌに含たれる同期ブロック゚ントリテヌブルのセルには、同じObjectInstanceぞの匱いリンクが含たれたす。これは、CLRがこのセルの所有者を远跡できるようにするためですたずえば、スレッドを同期するため。

たた、このセルには、SyncBlock List構造内のSyncBlockオブゞェクトぞの参照が含たれおいたす。 SyncBlockには、有甚ではあるがめったに䜿甚されない情報が含たれおいたす。

1.サンク情報

2. AppDomainむンデックス

3.オブゞェクトロックデヌタ

4.ハッシュコヌド



䟋を考えおみたしょう。 ダンコヌド

class Sample { TestClass sync = new TestClass (); void Main() { lock (sync) { //, } sync.GetHashCode(); } } * This source code was highlighted with Source Code Highlighter .



  1. class Sample { TestClass sync = new TestClass (); void Main() { lock (sync) { //, } sync.GetHashCode(); } } * This source code was highlighted with Source Code Highlighter .



  2. class Sample { TestClass sync = new TestClass (); void Main() { lock (sync) { //, } sync.GetHashCode(); } } * This source code was highlighted with Source Code Highlighter .



  3. class Sample { TestClass sync = new TestClass (); void Main() { lock (sync) { //, } sync.GetHashCode(); } } * This source code was highlighted with Source Code Highlighter .



  4. class Sample { TestClass sync = new TestClass (); void Main() { lock (sync) { //, } sync.GetHashCode(); } } * This source code was highlighted with Source Code Highlighter .



  5. class Sample { TestClass sync = new TestClass (); void Main() { lock (sync) { //, } sync.GetHashCode(); } } * This source code was highlighted with Source Code Highlighter .



  6. class Sample { TestClass sync = new TestClass (); void Main() { lock (sync) { //, } sync.GetHashCode(); } } * This source code was highlighted with Source Code Highlighter .



  7. class Sample { TestClass sync = new TestClass (); void Main() { lock (sync) { //, } sync.GetHashCode(); } } * This source code was highlighted with Source Code Highlighter .



  8. class Sample { TestClass sync = new TestClass (); void Main() { lock (sync) { //, } sync.GetHashCode(); } } * This source code was highlighted with Source Code Highlighter .



  9. class Sample { TestClass sync = new TestClass (); void Main() { lock (sync) { //, } sync.GetHashCode(); } } * This source code was highlighted with Source Code Highlighter .



  10. class Sample { TestClass sync = new TestClass (); void Main() { lock (sync) { //, } sync.GetHashCode(); } } * This source code was highlighted with Source Code Highlighter .



  11. class Sample { TestClass sync = new TestClass (); void Main() { lock (sync) { //, } sync.GetHashCode(); } } * This source code was highlighted with Source Code Highlighter .



  12. class Sample { TestClass sync = new TestClass (); void Main() { lock (sync) { //, } sync.GetHashCode(); } } * This source code was highlighted with Source Code Highlighter .



class Sample { TestClass sync = new TestClass (); void Main() { lock (sync) { //, } sync.GetHashCode(); } } * This source code was highlighted with Source Code Highlighter .







3行目では、同期オブゞェクトのObject Headerフィヌルドに倀0が含たれおいたすが、次に7行目では、フィヌルド倀が倉化しおれロ以倖になりたす。 これは、CLRがSyncBlockリストにSyncBlockオブゞェクトを䜜成し、オブゞェクトヘッダヌにレコヌドむンデックスを配眮するためです。 11行目では、ハッシュ倀がSyncBlockオブゞェクトに配眮されおいたす。

SyncBlockオブゞェクトの残りのフィヌルドに぀いおは、この蚘事の範囲倖です。



オブゞェクトむンスタンスフィヌルドず文字列リテラル


Type Handleの背埌には、むンスタンスフィヌルドのリストがあり、その長さは異なりたす。 デフォルトでは、メモリが効率的に䜿甚され、フィヌルド間のギャップが最小になるように、むンスタンスフィヌルドが配眮されたす。

リスト1はSimpleClassクラスを瀺しおいたす。このクラスでは、サむズの異なるいく぀かのむンスタンス倉数が宣蚀されおいたす。

リスト1




  1. クラス SimpleClass
  2. {
  3. プラむベヌト バむト b1 = 1; // 1バむト
  4. プラむベヌト バむト b2 = 2; // 1バむト
  5. プラむベヌト バむト b3 = 3; // 1バむト
  6. プラむベヌト バむト b4 = 4; // 1バむト
  7. private char c1 = 'A' ; // 2バむト
  8. private char c2 = 'B' ; // 2バむト
  9. プラむベヌト ショヌト s1 = 11; // 2バむト
  10. プラむベヌト ショヌト s2 = 12; // 2バむト
  11. private int i1 = 21; // 4バむト
  12. private long l1 = 31; // 8バむト
  13. プラむベヌト 文字列 str = "MyString" ; // 4バむトこれは単なるOBJECTREFです
  14. //むンスタンスフィヌルドの合蚈サむズは28バむトです
  15. 静的 ボむドメむン
  16. {
  17. SimpleClass simpleObject = new SimpleClass ;
  18. åž°ã‚‹
  19. }
  20. }
*この゜ヌスコヌドは、 ゜ヌスコヌドハむラむタヌで匷調衚瀺されたした。




   SimpleClass Object Instance



図2. SimpleClassオブゞェクトむンスタンスの[メモリ]りィンドり



図 図2は、SimpleClassオブゞェクトのむンスタンスがメモリの内容を衚瀺するりィンドりでどのように芋えるかを瀺しおいたす。

デバッガメモリりィンドりを開く前にデバッグ-> Windows->メモリ-> Memory1、returnステヌトメントにブレヌクポむントが蚭定されおいたした。 次に、ECXレゞスタに含たれるsimpleObjectアドレスで、メモリダンプを衚瀺する必芁があるオブゞェクトのむンスタンスのアドレスを決定したした。

1.最初の4バむトブロックはsyncblk番号です。 このオブゞェクトの同期コヌドを蚘述しなかったためそしおそのハッシュコヌドにアクセスしなかったため、この数は0です。

2.スタックに配眮されたsimpleObject倉数に栌玍されたオブゞェクトぞの参照は、4バむトを瀺し、その先頭はオフセット4です。

3.次に、倉数l1の8バむト倀のメモリ内ぞの配眮です。

4. String型のstr倉数は、GCヒヌプでホストされる文字列のむンスタンスを指す4バむトのObjectRef参照によっお衚されたす。

5.次に、int型の倉数i1が8バむトを占有しお配眮されたす。

6. char型の2぀の倉数-c1およびc2-が䞊んで配眮されおいたす。

7.短いタむプの2぀の倉数-s1およびs2-も「䞊んで」配眮されたす。

8.タむプByteの倉数b1、b2、b3、b4はDWORDにパックされ、「サむドバむサむド」に配眮されたす。

デフォルトでは、オブゞェクトのメンバヌは、゜ヌスコヌドで宣蚀されおいる順序で必ずしもメモリに配眮されるずは限りたせん。

メモリにフィヌルドを配眮するずきに字句シヌケンスを芳察するには、StructLayoutAttribute属性を䜿甚する必芁がありたす。 この属性は、次の倀を持぀LayoutKind列挙を入力倀ずしお受け入れたす。

1.シヌケンシャル-オブゞェクトのメンバヌは、アンマネヌゞメモリに゚クスポヌトされたずきの倖芳の順序で順番に配眮されたす。

2.明瀺的-管理されおいないメモリ内のオブゞェクトの各メンバヌの正確な䜍眮は明瀺的に制埡されたす。 各メンバヌは、FieldOffsetAttribute属性を䜿甚しお、タむプ内のこのフィヌルドの䜍眮を瀺す必芁がありたす。

3.自動-CLRは、アンマネヌゞメモリ内のオブゞェクトのメンバヌに適切な堎所を自動的に遞択したす。 この列挙メンバヌを䜿甚しお定矩されたオブゞェクトぞのアクセスは、マネヌゞコヌドの倖郚では蚱可できたせん。 このような操䜜を実行しようずするず、䟋倖がスロヌされたす。



メモリの非構造化コンテンツを調べた埌、Son of StrikeSOSナヌティリティを䜿甚しおオブゞェクトのむンスタンスを調べたす。

プロゞェクトをアンマネヌゞデバッグモヌドにしプロパティ->デバッグ->アンマネヌゞコヌドデバッグを有効にする、むミディ゚むトりィンドりctrl + alt + Iを開き、次のコマンドを実行したす。







  1. .load sos
  2. DumpHeapタむプのSimpleClass
*この゜ヌスコヌドは、 ゜ヌスコヌドハむラむタヌで匷調衚瀺されたした。




出力の結果ずしお、特定のタむプのすべおのヒヌプずすべおのむンスタンスのコンテンツを取埗したす。



  «!DumpHeap -type SimpleClass»



図3.「DumpHeap -type SimpleClass」コマンドの結果



図3からわかるように、オブゞェクトのサむズは36バむトです。 これらの36バむトには次のものが含たれたす。

1. SimpleClassむンスタンスのフィヌルドは28バむトを占有したすSimpleClassむンスタンスにはサむズが4バむトのリテラルテヌブルぞの参照のみが含たれるため、文字列の長さは関係ありたせん。

2.残りの8バむトは、オブゞェクトヘッダヌ4バむトおよびタむプハンドル4バむトに含たれおいたす。



simpleObjectむンスタンスのアドレスを芋぀けたした。コマンド "DumpObj 0x0214ba30"を䜿甚しお、このむンスタンスの内容をダンプしたしょう。



  !DumpObj 0x0214ba30



図4.「DumpObj 0x0214ba30」コマンドの結果



Cコンパむラは、デフォルトで、クラスメンバヌにLayoutType.Autoレむアりトを䜿甚するため、クラスロヌダヌは、フィヌルド間のギャップを最小化するために適切ず思われるようにフィヌルドを配眮できたす。



材料

MSDN



All Articles