C# 無法直接精確獲取託管對象在托管堆上的真實內存佔用大小。 這是由 CLR 內存模型(GC、對象頭、引用、對齊、壓縮等)決定的。
int size = sizeof(int); // 4 - 編譯時常量,僅限基本值類型
int size = sizeof(MyStruct); // 錯誤:結構體無預定義大小,需 unsafe 上下文
enum、不包含引用類型字段的 `struct` (在 `unsafe` 上下文)。
[StructLayout(LayoutKind.Sequential)]
struct PureValueStruct { public int A; public double B; }
[StructLayout(LayoutKind.Sequential)]
class MixedClass { public int A; public string B; } // 含引用類型
// 正確
int s1 = Marshal.SizeOf(); // 非托管大小:16 字節 (含對齊)
// 運行時拋異常:ArgumentException "無法封送類型..."
int s2 = Marshal.SizeOf();
[Serializable]
class Student { public string Name; }
long GetObjectSize(object obj) {
using (var ms = new MemoryStream()) {
new BinaryFormatter().Serialize(ms, obj);
return ms.Length;
}
}
// 結果:new Student() 約 139 字節,包含大量元數據(程序集名、類型名、字段名、版本等)
long before = GC.GetTotalMemory(true);
var list = new List();
for (int i = 0; i < 100000; i++) list.Add(new Student { Name = "Test" + i });
long after = GC.GetTotalMemory(true);
long avgSize = (after - before) / 100000; // 統計平均佔用
專業內存分析器可展示:
unsafe {
int size = sizeof(MyStruct); // 編譯期常量
// 或運行時
MyStruct s = new MyStruct();
int runtimeSize = sizeof(MyStruct); // 相同
}
| 因素 | 說明 |
|---|---|
| 非靜態實例字段 | 值類型直接內聯;引用類型存儲指針 (4/8 字節) |
| 對象頭 | SyncBlockIndex (4/8B) + MethodTablePtr (4/8B) = 8B (x86) / 16B (x64) |
| 對齊填充 | 實例大小向 8 字節 (x64) 對齊 |
| 靜態字段/屬性/方法 | 不佔用實例內存,屬於類型/方法表/靜態存儲區 |
| 字符串/數組/其他引用對象 | 單獨在堆上分配,實例僅存引用指針 |
「屬性會增加對象大小」 → 錯。自動屬性編譯為私有字段,字段才佔內存。只讀屬性、表達式主體屬性無後備字段則不佔用。
「Marshal.SizeOf 返回對象內存大小」 → 錯。返回非托管佈局大小,用於互操作編組。
「序列化大小 = 內存大小」 → 錯。序列化包含類型元數據,且不計對象頭、對齊、引用共享。