3 回答

TA貢獻1963條經驗 獲得超6個贊
數組是引用類型。所有引用類型都帶有兩個附加的單詞字段。類型引用和SyncBlock索引字段,除其他外,這些字段用于實現CLR中的鎖定。因此,引用類型的類型開銷是32位上的8個字節。最重要的是,數組本身還存儲了另外4個字節的長度。這使總開銷達到12個字節。
我剛剛從喬恩·斯凱特(Jon Skeet)的答案中學到,引用類型的數組還有4個字節的額外開銷??梢允褂肳inDbg確認。事實證明,附加字是存儲在數組中的類型的另一個類型引用。所有引用類型的數組都在內部存儲為object[],并附加了對實際類型的類型對象的引用。因此,a string[]實際上只是一個object[]帶有對type的附加類型引用的a string。有關詳細信息,請參見下文。
存儲在數組中的值:引用類型的數組保存對對象的引用,因此數組中的每個條目都是引用的大?。?2位為4字節)。值類型的數組內聯存儲值,因此每個元素都將占用所討論類型的大小。
這個問題可能也很有趣:C#List <double> size vs double [] size
血腥細節
考慮以下代碼
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的一個元素??梢钥闯觯笮?6個字節。也就是說,int32值本身為4個字節,常規引用類型的開銷為8個字節,數組的長度為另外4個字節。
原始轉儲顯示SyncBlock,方法表int[],長度和值42(十六進制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。
通過轉儲實例的原始字節,我們可以看到以下內容:首先是SyncBlock,然后是的方法表object[],然后是數組的長度。之后,我們通過引用方法表找到了另外的4個字節。如上所示,可以通過dumpmt命令對此進行驗證。最后,我們找到對實際字符串實例的單個引用。
結論
數組的開銷可以按如下方式分解(即32位)
4字節的SyncBlock
數組本身的方法表(類型引用)為4個字節
數組長度為4個字節
引用類型的數組又添加了4個字節來保存實際元素類型的方法表(引用類型的數組位于內部object[])
即,對于值類型數組,開銷是12個字節,對于引用類型數組,開銷是16個字節。

TA貢獻1821條經驗 獲得超6個贊
我們有一個項目,可處理大量數據(最大2GB)。作為主要存儲,我們使用Dictionary<T,T>
。實際上創建了數千個字典。將其更改List<T>
為鍵和List<T>
值(我們IDictionary<T,T>
自己實現)后,內存使用量減少了約30-40%。
- 3 回答
- 0 關注
- 446 瀏覽
添加回答
舉報