在C#中查找内存泄漏

use*_*246 16 .net c# garbage-collection memory-management

在以下程序中,虽然执行了垃圾收集,但不会重新获得内存初始大小的大小.1.内存的初始大小是总内存:16,940字节专用字节8134656

  1. 在循环内创建对象,以便在循环外部完成gc collect时释放这些对象,因为这些对象没有其范围.

  2. 但GC收集后的内存与初始大小不同总内存:29,476字节专用字节8540160处理数:115

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace MemoryLeakTest
{

    class Program
    {
        static void DisplayMemory()
        {
            Console.WriteLine("Total memory: {0:###,###,###,##0} bytes", GC.GetTotalMemory(true));            
            Console.WriteLine("Private bytes {0}", System.Diagnostics.Process.GetCurrentProcess().PrivateMemorySize64);
            Console.WriteLine("Handle count: {0}", System.Diagnostics.Process.GetCurrentProcess().HandleCount);
            Console.WriteLine();
        }

        static void Main()
        {
            DisplayMemory();
            GC.Collect();
            for (int i = 0; i < 5; i++)
            {
                Console.WriteLine("--- New object #{0} ---", i + 1);

                object o = new object();

                GC.Collect();
                DisplayMemory();
            }
            Console.WriteLine("--- press any key to quit ---");            
            Console.WriteLine();
            Console.Read();

            GC.Collect();
            DisplayMemory();
        }

    }
}

Output: 
=======
Total memory: 16,940 bytes
Private bytes 8134656
Handle count: 115

--- New object #1 ---
Total memory: 25,588 bytes
Private bytes 8540160
Handle count: 115

--- New object #2 ---
Total memory: 25,576 bytes
Private bytes 8540160
Handle count: 115

--- New object #3 ---
Total memory: 25,576 bytes
Private bytes 8540160
Handle count: 115

--- New object #4 ---
Total memory: 25,576 bytes
Private bytes 8540160
Handle count: 115

--- New object #5 ---
Total memory: 25,576 bytes
Private bytes 8540160
Handle count: 115

--- press any key to quit ---


Total memory: 29,476 bytes
Private bytes 8540160
Handle count: 115

*********************************
Run Code Online (Sandbox Code Playgroud)

私有字节和托管堆大小从初始大小增加的原因是什么?

hun*_*aro 24

整体问题

计算和报告内存使用情况的尝试是以比正在测量的GC对象分配大小多10,000倍的内存的方式处理的.

此外,打印句柄计数不适用于手头的问题(因为在测试中没有打开/关闭句柄)但是它确实导致了大量的内存分配(仅删除该计数将总分配减半).

原始程序试图测量60-120字节对象的分配(取决于它是32位还是64位程序),但它使用的函数每次调用时都会分配600 KB的内存,一半其中大对象堆(LOH).

提供了另一种测试方法,它表明在GC.Collect调用之后所有对象确实消失了.还提供了有关DisplayMemory函数的内存使用情况的详细信息.

结论

创建100k对象然后收集时,托管内存大小不会增加.当只创建和收集5个对象时,进程的私有字节增加大约12 KB,但SoS显示它不是来自托管堆.当你处理非常小的尺寸和物体数量时,你将无法确切地确定发生了什么; 相反,我建议测试非常大量的物体,以便很容易看出是否有物体泄漏.在这种情况下,没有泄漏,没有错,一切都很好.

分析工具和方法

我用两个工具来查看这个程序的内存使用情况:

  1. VS 2013 Pro - 性能和诊断工具 - 我先运行它,看到原始程序正在分配3.6 MB内存,而不仅仅是60-120字节,正如对象分配所期望的那样.我知道字符串会使用一些内存并写入控制台,但3.6 MB是一个震撼.
  2. Strike之子(SoS) - 这是一个在Visual Studio和WinDbg中运行的调试器扩展,它附带.Net Framework(请参阅计算机上每个框架版本目录中的sos.dll).

VS 2013 Pro - 性能和诊断工具 - 备注

在VS 2013 Pro中的性能和诊断工具下运行原始程序,并将"分析方法"设置为".NET内存分配"的结果如下所示.这提供了一个非常快速的线索,分配的内存比想象的要多.请参见图表上方3.6 MB的总分配.如果删除掉落到2,476字节的DisplayMemory调用.

性能和诊断工具 - 截图

罢工之子 - 笔记

只要您没有在机器上安装.Net 4.5,就可以在VS2010中使用SoS,或者您可以在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();
    }
}
Run Code Online (Sandbox Code Playgroud)

替代测试结果

摘要

在分配和收集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
Run Code Online (Sandbox Code Playgroud)

在分配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
Run Code Online (Sandbox Code Playgroud)

在最终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
Run Code Online (Sandbox Code Playgroud)

回顾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
Run Code Online (Sandbox Code Playgroud)

调用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
Run Code Online (Sandbox Code Playgroud)


top*_*ica 2

GC.Collect 是异步的,因此在 GC.Collect() 之后直接调用 DisplayMemory() 的逻辑;是值得怀疑的。 同步运行 GC.Collect有一些关于 GC.WaitForPendingFinalizers() 的详细信息。

编辑-回答你的问题:

Total memory: 84,280 bytes
Private bytes 15384576
Handle count: 245

--- New object #1 ---
Total memory: 86,408 bytes
Private bytes 15908864
Handle count: 245

--- New object #2 ---
Total memory: 86,408 bytes
Private bytes 15908864
Handle count: 245

--- New object #3 ---
Total memory: 86,408 bytes
Private bytes 15908864
Handle count: 245

--- New object #4 ---
Total memory: 86,408 bytes
Private bytes 15908864
Handle count: 245

--- New object #5 ---
Total memory: 86,408 bytes
Private bytes 15908864
Handle count: 245
Run Code Online (Sandbox Code Playgroud)

所以这里似乎没有太大区别!