总体问题
尝试计算和报告内存使用情况的处理方式是占用比正在测量的 GC 对象分配大小多 10,000 倍的内存。
此外,打印句柄计数不适用于手头的问题(因为在测试中没有打开/关闭句柄),但它确实导致了大量的内存分配(仅删除该计数将总分配减少了一半)。
原始程序试图测量 60-120 字节对象的分配(取决于它是 32 位还是 64 位程序),但它使用的函数会导致每次调用时分配 600 KB 的内存,一半其中位于大对象堆 (LOH) 上。
提供了另一种测试方法,它表明所有对象在 GC.Collect 调用之后确实消失了。还提供了有关 DisplayMemory 函数的内存使用情况的详细信息。
结论
创建并收集 100k 个对象时,托管内存大小不会增加。当只创建和收集 5 个对象时,进程的私有字节增加了大约 12 KB,但 SoS 显示它不是来自托管堆。当您处理非常小的尺寸和对象数量时,您将无法准确确定发生了什么;相反,我建议使用大量对象进行测试,以便很容易查看是否有东西泄漏。在这种情况下,没有泄漏,没有任何问题,一切都很好。
分析工具和方法
我使用了两个工具来查看该程序的内存使用情况:
- VS 2013 Pro - 性能和诊断工具 - 我首先运行它,发现原始程序分配了 3.6 MB 内存,而不仅仅是对象分配所期望的 60-120 字节。我知道字符串和写入控制台会使用一些内存,但 3.6 MB 令人震惊。
- Son of Strike (SoS) - 这是一个在 Visual Studio 和 WinDbg 中工作的调试器扩展,它随 .Net 框架一起提供(请参阅您机器上每个框架版本目录中的 sos.dll)。
VS 2013 Pro - 性能和诊断工具 - 注释
下面是在 VS 2013 Pro 的性能和诊断工具下运行原始程序的结果,其中“分析方法”设置为“.NET 内存分配”。这提供了一个非常快速的线索,即分配的内存比想象的要多。请参阅图表上方的 3.6 MB 总分配。如果您删除下降到 2,476 字节的 DisplayMemory 调用。
罢工之子 - 笔记
可以在VS2010中使用SoS,只要你的机器上没有安装.Net 4.5,也可以在VS2012中使用Update3;只需确保在您的项目中启用非托管调试并确保您正在启动 32 位进程,然后在 VS 调试器的即时窗口中运行“.load sos”。我用来查看此问题的命令是“!eeheap -gc”和“!dumpheap -stat”。
替代测试程序
class Program
{
static void Main()
{
// A few objects get released by the initial GC.Collect call - the count drops from 108 to 94 objects in one test
GC.Collect();
// Set a breakpoint here, run these two sos commands:
// !eeheap -gc
// !dumpheap -stat
for (int i = 0; i < 100000; i++)
{
object o = new object();
}
// Set a breakpoint here, run these two sos commands before this line, then step over and run them again
// !eeheap -gc
// !dumpheap -stat
GC.Collect();
}
}
替代测试结果
概括
在分配和收集 100,000 个 System.Objects 之后,我们最终得到了比我们开始时少 4 个对象和一个比我们开始时小 900 字节的托管堆大小。
垃圾收集按预期工作。
基线 - 在第一次 GC.Collect 之后
!eeheap -gc
Number of GC Heaps: 1
generation 0 starts at 0x024f23d0
generation 1 starts at 0x024f100c
generation 2 starts at 0x024f1000
ephemeral segment allocation context: none
segment begin allocated size
024f0000 024f1000 024f23dc 0x13dc(5084)
Large object heap starts at 0x034f1000
segment begin allocated size
034f0000 034f1000 034f5380 0x4380(17280)
Total Size: Size: 0x575c (22364) bytes.
------------------------------
GC Heap Size: Size: 0x575c (22364) bytes.
!dumpheap -stat
Statistics:
MT Count TotalSize Class Name
[...]
6ed026b8 1 112 System.AppDomain
6ed025b0 2 168 System.Threading.ThreadAbortException
6ed05d3c 1 284 System.Collections.Generic.Dictionary`2+Entry[[System.Type, mscorlib],[System.Security.Policy.EvidenceTypeDescriptor, mscorlib]][]
6ed03a6c 2 380 System.Int32[]
6ed0349c 20 560 System.RuntimeType
0047fab8 14 1024 Free
6ed02248 32 1692 System.String
6ecefe88 6 17340 System.Object[]
Total 95 objects
在分配 100,000 个 System.Objects 之后,在最终 GC.Collect 之前
!eeheap -gc
Number of GC Heaps: 1
generation 0 starts at 0x024f23d0
generation 1 starts at 0x024f100c
generation 2 starts at 0x024f1000
ephemeral segment allocation context: none
segment begin allocated size
024f0000 024f1000 02617ff4 0x126ff4(1208308)
Large object heap starts at 0x034f1000
segment begin allocated size
034f0000 034f1000 034f5380 0x4380(17280)
Total Size: Size: 0x12b374 (1225588) bytes.
------------------------------
GC Heap Size: Size: 0x12b374 (1225588) bytes.
!dumpheap -stat
Statistics:
MT Count TotalSize Class Name
[...]
6ed024e4 1 84 System.OutOfMemoryException
6ed02390 1 84 System.Exception
6ed026b8 1 112 System.AppDomain
6ed025b0 2 168 System.Threading.ThreadAbortException
6ed05d3c 1 284 System.Collections.Generic.Dictionary`2+Entry[[System.Type, mscorlib],[System.Security.Policy.EvidenceTypeDescriptor, mscorlib]][]
6ed03a6c 2 380 System.Int32[]
6ed0349c 20 560 System.RuntimeType
0047fab8 14 1024 Free
6ed02248 32 1692 System.String
6ecefe88 6 17340 System.Object[]
6ed025e8 100002 1200024 System.Object
Total 100095 objects
在最终 GC.Collect 之后
!eeheap -gc
Number of GC Heaps: 1
generation 0 starts at 0x024f2048
generation 1 starts at 0x024f2030
generation 2 starts at 0x024f1000
ephemeral segment allocation context: none
segment begin allocated size
024f0000 024f1000 024f2054 0x1054(4180)
Large object heap starts at 0x034f1000
segment begin allocated size
034f0000 034f1000 034f5380 0x4380(17280)
Total Size: Size: 0x53d4 (21460) bytes.
------------------------------
GC Heap Size: Size: 0x53d4 (21460) bytes.
!dumpheap -stat
Statistics:
MT Count TotalSize Class Name
[...]
6ed024e4 1 84 System.OutOfMemoryException
6ed02390 1 84 System.Exception
6ed026b8 1 112 System.AppDomain
0047fab8 9 118 Free
6ed025b0 2 168 System.Threading.ThreadAbortException
6ed05d3c 1 284 System.Collections.Generic.Dictionary`2+Entry[[System.Type, mscorlib],[System.Security.Policy.EvidenceTypeDescriptor, mscorlib]][]
6ed03a6c 2 380 System.Int32[]
6ed0349c 20 560 System.RuntimeType
6ed02248 32 1692 System.String
6ecefe88 6 17340 System.Object[]
Total 91 objects
回顾 DisplayMemory 函数的内存使用情况
与 System.Object 分配相比,DisplayMemory 是一种内存占用。它正在创建字符串(在堆上),它调用来获取内存的函数本身使用了大量的内存(大约 600 KB)。
调用 DisplayMemory 之前的内存使用情况
!eeheap -gc
Number of GC Heaps: 1
generation 0 starts at 0x02321018
generation 1 starts at 0x0232100c
generation 2 starts at 0x02321000
ephemeral segment allocation context: none
segment begin allocated size
02320000 02321000 02323ff4 0x2ff4(12276)
Large object heap starts at 0x03321000
segment begin allocated size
03320000 03321000 03325380 0x4380(17280)
Total Size: Size: 0x7374 (29556) bytes.
------------------------------
GC Heap Size: Size: 0x7374 (29556) bytes.
!dumpheap -stat
Statistics:
MT Count TotalSize Class Name
[...]
6ed05d3c 3 468 System.Collections.Generic.Dictionary`2+Entry[[System.Type, mscorlib],[System.Security.Policy.EvidenceTypeDescriptor, mscorlib]][]
6ed0349c 20 560 System.RuntimeType
6ed02248 38 2422 System.String
6ecefe88 6 17340 System.Object[]
Total 102 objects
调用 DisplayMemory 后的内存使用情况
!eeheap -gc
Number of GC Heaps: 1
generation 0 starts at 0x023224fc
generation 1 starts at 0x023224f0
generation 2 starts at 0x02321000
ephemeral segment allocation context: none
segment begin allocated size
02320000 02321000 02371ff4 0x50ff4(331764)
Large object heap starts at 0x03321000
segment begin allocated size
03320000 03321000 033653c0 0x443c0(279488)
Total Size: Size: 0x953b4 (611252) bytes.
------------------------------
GC Heap Size: Size: 0x953b4 (611252) bytes.
!dumpheap -stat
Statistics:
MT Count TotalSize Class Name
[...]
6ed02c08 9 954 System.Char[]
006dfac0 17 1090 Free
6ed03aa4 156 1872 System.Int32
6ecffc20 152 3648 System.Collections.ArrayList
6ed05ed4 9 7776 System.Collections.Hashtable+bucket[]
7066e388 152 16416 System.Diagnostics.ProcessInfo
6ed02248 669 20748 System.String
706723e4 152 29184 System.Diagnostics.NtProcessInfoHelper+SystemProcessInformation
6ecefe88 463 48472 System.Object[]
706743a4 2104 75744 System.Diagnostics.ThreadInfo
70666568 2104 151488 System.Diagnostics.NtProcessInfoHelper+SystemThreadInformation
6ed0d640 2 262168 System.Int64[]
Total 6132 objects