为什么 WinDbg 将 System.Int32 变量显示为 24 个字节?
Why does WinDbg show System.Int32 variables as 24 bytes?
我正在使用 WinDbg 分析 .NET 进程的内存转储,我注意到它将堆上所有 System.Int32 变量的大小报告为 24 字节 .下面是对变量之一的相关 DumpObj 调用的示例:
0:000> !DumpObj /d 00000061c81c0e80
Name: System.Int32
MethodTable: 00007fff433f37c8
EEClass: 00007fff42e30130
Size: 24(0x18) bytes
File: C:\Windows\Microsoft.Net\assembly\GAC_64\mscorlib\v4.0_4.0.0.0__b77a5c561934e089\mscorlib.dll
Fields:
MT Field Offset Type VT Attr Value Name
00007fff433f37c8 4000456 8 System.Int32 1 instance 141 m_value
据我所知,System.Int32 的大小应该是 4 个字节。这种差异的根源是什么?
它不是 int32 的大小,执行 dd 或 dq 地址并查看您的 int32 卡在第二个 dword 或 qword 中
对于 x86 / x64,每个对象分别有 12 字节或 24 字节的隐式开销
0:004> .shell -ci "!DumpObj /d 01c72360" grep -i size
Size: 12(0xc) bytes
.shell: Process exited
0:004> dd 01c72360 l4
01c72360 5890c770 000001b5 80000000 5890afb0
0:004> .shell -ci "!DumpObj /d 01c72360" grep -i method
MethodTable: 5890c770
.shell: Process exited
0:004> .shell -ci "!DumpObj /d 01c72360" grep -i value
MT Field Offset Type VT Attr Value Name
5890c770 400044f 4 System.Int32 1 instance 437 m_value
.shell: Process exited
0:004> ? 1b5
Evaluate expression: 437 = 000001b5
将 int32 分开让我们剖析 x86 中的宽字符串 "stream"
actualsizereqdfor(L"stream[=11=]") = 7 * sizeof(wchar_t) == 7 * 2 == 0n14;
sizeof(method table ) == 0n04;
sizeof(sizeof(L"stream)) == 0n04;
sizeof(padding ?? terminator ?? whatever ?? ) == 0n04;
so total size == 0n26
dumpobj 的结果
0:004> !DumpObj /d 01c73ad0
Name: System.String
MethodTable: 5890afb0
Size: 26(0x1a) bytes
String: stream
Fields:
MT Field Offset Type VT Attr Value Name
5890c770 40000aa 4 System.Int32 1 instance 6 m_stringLength
5890b9a8 40000ab 8 System.Char 1 instance 73 m_firstChar
5890afb0 40000ac c System.String 0 shared static Empty
原始显示
0:004> db 01c73ad0 l1a
01c73ad0 b0 af 90 58 06 00 00 00-73 00 74 00 72 00 65 00 ...X....s.t.r.e.
01c73ae0 61 00 6d 00 00 00 00 00-00 00 a.m.......
堆上的任何对象都有开销。在 32 位 MS.NET 运行时,这是 8 个字节,在 64 位,16 个字节(免责声明:这不是严格的合同,将来可能会更改或在 .NET 运行时的一致实现中)。
由于您的 int
是装箱的,因此会有 16 字节的开销。所以您可能期望总共使用 20 个字节。好吧,除了在 64 位系统上,对象(和结构)被填充到 8 字节边界,所以你实际上每个 int
.
得到 24 字节
相比之下,当您使用带有 16 个整数的 struct
时,您只会使用 16 + 4 * 16 = 80 字节的内存,每个整数总共需要 5 个字节。
再说一次,其中大部分是实施细节,因此您不能依赖它;有效的 .NET 运行时完全有可能将单个 int
存储在 1 MiB 的内存中,如果它愿意的话,它也可以将其存储在一些紧凑的表示形式或实习中,只要它符合该类型的所有契约行为。即使与实际的 MS 运行时实现相比,它也非常简化 - 例如,如果您的对象足够大,它将需要更多的开销。
我正在使用 WinDbg 分析 .NET 进程的内存转储,我注意到它将堆上所有 System.Int32 变量的大小报告为 24 字节 .下面是对变量之一的相关 DumpObj 调用的示例:
0:000> !DumpObj /d 00000061c81c0e80
Name: System.Int32
MethodTable: 00007fff433f37c8
EEClass: 00007fff42e30130
Size: 24(0x18) bytes
File: C:\Windows\Microsoft.Net\assembly\GAC_64\mscorlib\v4.0_4.0.0.0__b77a5c561934e089\mscorlib.dll
Fields:
MT Field Offset Type VT Attr Value Name
00007fff433f37c8 4000456 8 System.Int32 1 instance 141 m_value
据我所知,System.Int32 的大小应该是 4 个字节。这种差异的根源是什么?
它不是 int32 的大小,执行 dd 或 dq 地址并查看您的 int32 卡在第二个 dword 或 qword 中
对于 x86 / x64,每个对象分别有 12 字节或 24 字节的隐式开销
0:004> .shell -ci "!DumpObj /d 01c72360" grep -i size
Size: 12(0xc) bytes
.shell: Process exited
0:004> dd 01c72360 l4
01c72360 5890c770 000001b5 80000000 5890afb0
0:004> .shell -ci "!DumpObj /d 01c72360" grep -i method
MethodTable: 5890c770
.shell: Process exited
0:004> .shell -ci "!DumpObj /d 01c72360" grep -i value
MT Field Offset Type VT Attr Value Name
5890c770 400044f 4 System.Int32 1 instance 437 m_value
.shell: Process exited
0:004> ? 1b5
Evaluate expression: 437 = 000001b5
将 int32 分开让我们剖析 x86 中的宽字符串 "stream"
actualsizereqdfor(L"stream[=11=]") = 7 * sizeof(wchar_t) == 7 * 2 == 0n14;
sizeof(method table ) == 0n04;
sizeof(sizeof(L"stream)) == 0n04;
sizeof(padding ?? terminator ?? whatever ?? ) == 0n04;
so total size == 0n26
dumpobj 的结果
0:004> !DumpObj /d 01c73ad0
Name: System.String
MethodTable: 5890afb0
Size: 26(0x1a) bytes
String: stream
Fields:
MT Field Offset Type VT Attr Value Name
5890c770 40000aa 4 System.Int32 1 instance 6 m_stringLength
5890b9a8 40000ab 8 System.Char 1 instance 73 m_firstChar
5890afb0 40000ac c System.String 0 shared static Empty
原始显示
0:004> db 01c73ad0 l1a
01c73ad0 b0 af 90 58 06 00 00 00-73 00 74 00 72 00 65 00 ...X....s.t.r.e.
01c73ae0 61 00 6d 00 00 00 00 00-00 00 a.m.......
堆上的任何对象都有开销。在 32 位 MS.NET 运行时,这是 8 个字节,在 64 位,16 个字节(免责声明:这不是严格的合同,将来可能会更改或在 .NET 运行时的一致实现中)。
由于您的 int
是装箱的,因此会有 16 字节的开销。所以您可能期望总共使用 20 个字节。好吧,除了在 64 位系统上,对象(和结构)被填充到 8 字节边界,所以你实际上每个 int
.
相比之下,当您使用带有 16 个整数的 struct
时,您只会使用 16 + 4 * 16 = 80 字节的内存,每个整数总共需要 5 个字节。
再说一次,其中大部分是实施细节,因此您不能依赖它;有效的 .NET 运行时完全有可能将单个 int
存储在 1 MiB 的内存中,如果它愿意的话,它也可以将其存储在一些紧凑的表示形式或实习中,只要它符合该类型的所有契约行为。即使与实际的 MS 运行时实现相比,它也非常简化 - 例如,如果您的对象足够大,它将需要更多的开销。