jjzjj

c# - 在C#中查找内存泄漏

coder 2024-05-19 原文

在以下程序中,尽管执行了垃圾回收,但未重新获得内存的初始大小。
1.内存的初始大小为
总内存:16,940字节
专用字节8134656

  • 在循环内创建的对象,以便在循环外完成gc收集时释放这些对象,因为这些对象没有范围。
  • 但是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
    
    *********************************
    

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

    最佳答案

    总体问题

    尝试计算和报告内存使用情况的方式所占用的内存是所测量的GC对象分配大小的10,000倍。

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

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

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

    结论

    创建并收集10万个对象时,托管内存大小不会增加。当仅创建和收集5个对象时,该过程的专用字节增加了大约12 KB,但是SoS表明它不是来自托管堆。当您处理非常小的尺寸和对象数时,您将无法准确确定正在发生的事情。相反,我建议使用大量对象进行测试,这样很容易查看是否有泄漏。在这种情况下,没有泄漏,没有错,一切都很好。

    分析工具和方法

    我使用了两个工具来检查此程序的内存使用情况:

  • VS 2013 Pro-性能和诊断工具-我首先运行此程序,发现原始程序分配了3.6 MB的内存,而不仅仅是对象分配所期望的60-120字节。我知道字符串会使用一些内存并写入控制台,但是3.6 MB的内存令人震惊。
  • 罢工之子(SoS)-这是一个调试器扩展,可在Visual Studio和WinDbg中使用,并且与.Net Framework一起提供(请参阅计算机上每个框架版本目录中的sos.dll)。

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

    下面是在VS 2013 Pro的“性能和诊断工具”下通过将“分析方法”设置为“.NET内存分配”运行原始程序的结果。这提供了一个非常快速的线索,表明分配的内存超出了预期。请参阅图表上方的3.6 MB总分配。如果删除DisplayMemory调用,该调用将下降到2,476字节。

    罢工之子-笔记

    只要尚未在计算机上安装.Net 4.5,就可以在VS2010中使用SoS,或者可以在带有Update3的VS2012中使用它。只要确保在项目中启用非托管调试,并确保要启动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
    

    关于c# - 在C#中查找内存泄漏,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18739892/

    有关c# - 在C#中查找内存泄漏的更多相关文章

    1. ruby-on-rails - Ruby net/ldap 模块中的内存泄漏 - 2

      作为我的Rails应用程序的一部分,我编写了一个小导入程序,它从我们的LDAP系统中吸取数据并将其塞入一个用户表中。不幸的是,与LDAP相关的代码在遍历我们的32K用户时泄漏了大量内存,我一直无法弄清楚如何解决这个问题。这个问题似乎在某种程度上与LDAP库有关,因为当我删除对LDAP内容的调用时,内存使用情况会很好地稳定下来。此外,不断增加的对象是Net::BER::BerIdentifiedString和Net::BER::BerIdentifiedArray,它们都是LDAP库的一部分。当我运行导入时,内存使用量最终达到超过1GB的峰值。如果问题存在,我需要找到一些方法来更正我的代

    2. ruby - 当使用::指定模块时,为什么 Ruby 不在更高范围内查找类? - 2

      我刚刚被困在这个问题上一段时间了。以这个基地为例:moduleTopclassTestendmoduleFooendend稍后,我可以通过这样做在Foo中定义扩展Test的类:moduleTopmoduleFooclassSomeTest但是,如果我尝试通过使用::指定模块来最小化缩进:moduleTop::FooclassFailure这失败了:NameError:uninitializedconstantTop::Foo::Test这是一个错误,还是仅仅是Ruby解析变量名的方式的逻辑结果? 最佳答案 Isthisabug,or

    3. ruby-on-rails - Ruby 中的内存模型 - 2

      ruby如何管理内存。例如:如果我们在执行过程中采用C程序,则以下是内存模型。类似于这个ruby如何处理内存。C:__________________|||stack|||------------------||||------------------|||||Heap|||||__________________|||data|__________________|text|__________________Ruby:? 最佳答案 Ruby中没有“内存”这样的东西。Class#allocate分配一个对象并返回该对象。这就是程序

    4. ruby - 查找字符串中的内容类型(数字、日期、时间、字符串等) - 2

      我正在尝试解析一个CSV文件并使用SQL命令自动为其创建一个表。CSV中的第一行给出了列标题。但我需要推断每个列的类型。Ruby中是否有任何函数可以找到每个字段中内容的类型。例如,CSV行:"12012","Test","1233.22","12:21:22","10/10/2009"应该产生像这样的类型['integer','string','float','time','date']谢谢! 最佳答案 require'time'defto_something(str)if(num=Integer(str)rescueFloat(s

    5. c# - 如何在 ruby​​ 中调用 C# dll? - 2

      如何在ruby​​中调用C#dll? 最佳答案 我能想到几种可能性:为您的DLL编写(或找人编写)一个COM包装器,如果它还没有,则使用Ruby的WIN32OLE库来调用它;看看RubyCLR,其中一位作者是JohnLam,他继续在Microsoft从事IronRuby方面的工作。(估计不会再维护了,可能不支持.Net2.0以上的版本);正如其他地方已经提到的,看看使用IronRuby,如果这是您的技术选择。有一个主题是here.请注意,最后一篇文章实际上来自JohnLam(看起来像是2009年3月),他似乎很自在地断言RubyCL

    6. C# 到 Ruby sha1 base64 编码 - 2

      我正在尝试在Ruby中复制Convert.ToBase64String()行为。这是我的C#代码:varsha1=newSHA1CryptoServiceProvider();varpasswordBytes=Encoding.UTF8.GetBytes("password");varpasswordHash=sha1.ComputeHash(passwordBytes);returnConvert.ToBase64String(passwordHash);//returns"W6ph5Mm5Pz8GgiULbPgzG37mj9g="当我在Ruby中尝试同样的事情时,我得到了相同sha

    7. 基于C#实现简易绘图工具【100010177】 - 2

      C#实现简易绘图工具一.引言实验目的:通过制作窗体应用程序(C#画图软件),熟悉基本的窗体设计过程以及控件设计,事件处理等,熟悉使用C#的winform窗体进行绘图的基本步骤,对于面向对象编程有更加深刻的体会.Tutorial任务设计一个具有基本功能的画图软件**·包括简单的新建文件,保存,重新绘图等功能**·实现一些基本图形的绘制,包括铅笔和基本形状等,学习橡皮工具的创建**·设计一个合理舒适的UI界面**注明:你可能需要先了解一些关于winform窗体应用程序绘图的基本知识,以及关于GDI+类和结构的知识二.实验环境Windows系统下的visualstudio2017C#窗体应用程序三.

    8. ruby-on-rails - 在 Rails 中更高效地查找或创建多条记录 - 2

      我有一个应用需要发送用户事件邀请。当用户邀请friend(用户)参加事件时,如果尚不存在将用户连接到该事件的新记录,则会创建该记录。我的模型由用户、事件和events_user组成。classEventdefinvite(user_id,*args)user_id.eachdo|u|e=EventsUser.find_or_create_by_event_id_and_user_id(self.id,u)e.save!endendend用法Event.first.invite([1,2,3])我不认为以上是完成我的任务的最有效方法。我设想了一种方法,例如Model.find_or_cr

    9. 键删除后 ruby​​ 哈希内存泄漏 - 2

      你好,我无法成功如何在散列中删除key后释放内存。当我从哈希中删除键时,内存不会释放,也不会在手动调用GC.start后释放。当从Hash中删除键并且这些对象在某处泄漏时,这是预期的行为还是GC不释放内存?如何在Ruby中删除Hash中的键并在内存中取消分配它?例子:irb(main):001:0>`ps-orss=-p#{Process.pid}`.to_i=>4748irb(main):002:0>a={}=>{}irb(main):003:0>1000000.times{|i|a[i]="test#{i}"}=>1000000irb(main):004:0>`ps-orss=-p

    10. ruby - 查找重叠的正则表达式匹配项 - 2

      我想找到给定字符串中的所有匹配项,包括重叠匹配项。我怎样才能实现它?#Example"a-b-c-d".???(/\w-\w/)#=>["a-b","b-c","c-d"]expected#Solutionwithoutoverlappedresults"a-b-c-d".scan(/\w-\w/)#=>["a-b","c-d"],but"b-c"ismissing 最佳答案 在积极的前瞻中使用捕获:"a-b-c-d".scan(/(?=(\w-\w))/).flatten#=>["a-b","b-c","c-d"]参见Rubyde

    随机推荐