配列は参照型です。すべての参照型には、2つの追加の単語フィールドがあります。タイプ参照とSyncBlockインデックスフィールド。これらは、とりわけCLRでロックを実装するために使用されます。したがって、参照型の型オーバーヘッドは32ビットで8バイトです。その上、配列自体もさらに4バイトの長さを格納します。これにより、合計オーバーヘッドが12バイトになります。
そして、私はJon Skeetの答えから学びました。参照型の配列には、さらに4バイトのオーバーヘッドがあります。これは、WinDbgを使用して確認できます。追加の単語は、配列に格納されている型の別の型参照であることがわかります。参照型のすべての配列はobject[]
、実際の型の型オブジェクトへの追加の参照とともに、として内部に格納されます。したがって、astring[]
は、実際にはobject[]
、型への追加の型参照を持つだけstring
です。詳しくは下記をご覧ください。
配列に格納された値:参照型の配列はオブジェクトへの参照を保持するため、配列の各エントリは参照のサイズです(つまり、32ビットで4バイト)。値型の配列は値をインラインで格納するため、各要素は問題の型のサイズを占めます。
この質問も興味深いかもしれません:C#リスト<double>サイズとdouble[]サイズ
ゴーリーの詳細
次のコードを検討してください
var strings = new string[1];
var ints = new int[1];
strings[0] = "hello world";
ints[0] = 42;
WinDbgをアタッチすると、次のようになります。
まず、値型配列を見てみましょう。
0:000> !dumparray -details 017e2acc
Name: System.Int32[]
MethodTable: 63b9aa40
EEClass: 6395b4d4
Size: 16(0x10) bytes
Array: Rank 1, Number of elements 1, Type Int32
Element Methodtable: 63b9aaf0
[0] 017e2ad4
Name: System.Int32
MethodTable 63b9aaf0
EEClass: 6395b548
Size: 12(0xc) bytes
(C:\Windows\assembly\GAC_32\mscorlib\2.0.0.0__b77a5c561934e089\mscorlib.dll)
Fields:
MT Field Offset Type VT Attr Value Name
63b9aaf0 40003f0 0 System.Int32 1 instance 42 m_value <=== Our value
0:000> !objsize 017e2acc
sizeof(017e2acc) = 16 ( 0x10) bytes (System.Int32[])
0:000> dd 017e2acc -0x4
017e2ac8 00000000 63b9aa40 00000001 0000002a <=== That's the value
まず、配列と値42の1つの要素をダンプします。ご覧のとおり、サイズは16バイトです。つまり、int32
値自体に4バイト、通常の参照型のオーバーヘッドに8バイト、配列の長さにさらに4バイトです。
rawダンプには、SyncBlock、メソッドテーブルint[]
、長さ、および42の値(16進数で2a)が表示されます。SyncBlockがオブジェクト参照の直前にあることに注意してください。
string[]
次に、を見て、追加の単語が何に使用されているかを調べましょう。
0:000> !dumparray -details 017e2ab8
Name: System.String[]
MethodTable: 63b74ed0
EEClass: 6395a8a0
Size: 20(0x14) bytes
Array: Rank 1, Number of elements 1, Type CLASS
Element Methodtable: 63b988a4
[0] 017e2a90
Name: System.String
MethodTable: 63b988a4
EEClass: 6395a498
Size: 40(0x28) bytes <=== Size of the string
(C:\Windows\assembly\GAC_32\mscorlib\2.0.0.0__b77a5c561934e089\mscorlib.dll)
String: hello world
Fields:
MT Field Offset Type VT Attr Value Name
63b9aaf0 4000096 4 System.Int32 1 instance 12 m_arrayLength
63b9aaf0 4000097 8 System.Int32 1 instance 11 m_stringLength
63b99584 4000098 c System.Char 1 instance 68 m_firstChar
63b988a4 4000099 10 System.String 0 shared static Empty
>> Domain:Value 00226438:017e1198 <<
63b994d4 400009a 14 System.Char[] 0 shared static WhitespaceChars
>> Domain:Value 00226438:017e1760 <<
0:000> !objsize 017e2ab8
sizeof(017e2ab8) = 60 ( 0x3c) bytes (System.Object[]) <=== Notice the underlying type of the string[]
0:000> dd 017e2ab8 -0x4
017e2ab4 00000000 63b74ed0 00000001 63b988a4 <=== Method table for string
017e2ac4 017e2a90 <=== Address of the string in memory
0:000> !dumpmt 63b988a4
EEClass: 6395a498
Module: 63931000
Name: System.String
mdToken: 02000024 (C:\Windows\assembly\GAC_32\mscorlib\2.0.0.0__b77a5c561934e089\mscorlib.dll)
BaseSize: 0x10
ComponentSize: 0x2
Number of IFaces in IFaceMap: 7
Slots in VTable: 196
まず、配列と文字列をダンプします。次に、のサイズをダンプしstring[]
ます。WinDbgがタイプをSystem.Object[]
ここにリストしていることに注意してください。この場合のオブジェクトサイズには文字列自体が含まれるため、合計サイズは配列の20に文字列の40を加えたものになります。
インスタンスのrawバイトをダンプすると、次のことがわかります。最初にSyncBlockがあり、次にのメソッドテーブルobject[]
、次に配列の長さがわかります。その後、文字列のメソッドテーブルへの参照を含む追加の4バイトを見つけます。これは、上記のようにdumpmtコマンドで確認できます。最後に、実際の文字列インスタンスへの単一の参照を見つけます。
結論は
アレイのオーバーヘッドは、次のように分類できます(32ビットの場合)
- 4バイトのSyncBlock
- 配列自体のメソッドテーブル(型参照)用に4バイト
- 配列の長さは4バイト
- 参照型の配列は、実際の要素型のメソッドテーブルを保持するためにさらに4バイトを追加します(参照型の配列は内部にあります
object[]
)
つまり、オーバーヘッドは、値型配列の場合は12バイト、参照型配列の場合は16バイトです。