• 使用.NET工具调试System.OutOfMemoryException


    在这篇文章中,我将讨论一个更棘手的异常:System.OutOfMemoryException。顾名思义,当.NET应用程序内存不足时抛出异常。有在MSDN文章中,OutOfMemoryException有两种不同的原因:

    • 试图将StringBuilder对象扩展到其StringBuilder.MaxCapacity属性定义的长度之外。此类错误通常会附加以下消息:“内存不足,无法继续执行程序。”
    • 公共语言运行库(CLR)无法分配足够的连续内存。

    在我过去的.NET开发生涯中,我没有遇到过第一个问题,为什么我不会花太多时间在上面。简而言之,这样做将导致System.OutOfMemoryException:

    StringBuilder sb = new StringBuilder(1, 1);
    sb.Insert(0, "x", 2);

    为什么?好吧,我们定义了一个新的StringBuilder,它的最大容量是一个字符,然后尝试插入两个字符。这样一来,让我们来谈谈为什么您可能会遇到异常:因为CLR无法分配您的程序正在请求的内存。要将此转换为您妈妈会理解的内容,您的应用程序使用的资源比可用的资源还要多。
    .NET程序经常使用大量内存。NET中的内存管理基于垃圾收集,这意味着您不需要告诉框架何时清理。当.NET检测到不再需要某个对象时,会将其标记为删除,并在下次运行垃圾收集器时将其删除。这也意味着OutOfMemoryException并不总是等同于一个问题。32位进程有2 GB的可用虚拟内存,64位进程最多有8 TB。如果在64位操作系统上运行,请始终确保将应用程序编译为64位(它可能已经这样做了)。“内存不足”并不是指物理内存。区分大量内存使用和内存泄漏是很重要的。第一种情况可以接受,而第二种情况总是需要调试。

    要开始调试OutOfMemoryException,我建议您通过任务管理器或使用perfmon.msc查看应用程序。这两个工具都可以跟踪当前的内存消耗,但是为了获得一个更好的概览,perfmon是最好的。启动时,右键单击图表区域,然后单击添加计数器。。。展开.NET CLR内存节点,然后单击“提交的字节总数”。最后,在“选定对象的实例”列表中选择要监视的进程,然后单击“确定”按钮。
    在本文的其余部分中,我将使用并修改一个示例程序,向列表中添加字符串:

    class Program
    {
        static void Main(string[] args)
        {
            try
            {
                var list = new List<string>();
                int counter = 0;
                while (true)
                {
                    list.Add(Guid.NewGuid().ToString());
                    counter++;
                    if (counter%10000000 == 0)
                    {
                        list.Clear();
                    }
    
                }
            }
            catch (OutOfMemoryException e)
            {
                Environment.FailFast(String.Format($"Out of Memory: {e.Message}"));
            }
        }
    }

    在当前状态下,程序不断向列表中添加字符串,并且每10000000次清除列表。在Perfmon中查看当前内存使用情况时,您将看到当前图片:

    最好的垃圾收集。这里,我删除了对list.Clear()的调用:

    class Program
    {
        static void Main(string[] args)
        {
            try
            {
                var list = new List<string>();
                while (true)
                {
                    list.Add(Guid.NewGuid().ToString());
                }
            }
            catch (OutOfMemoryException e)
            {
                Environment.FailFast(String.Format($"Out of Memory: {e.Message}"));
            }
        }
    }

    我们现在得到一张完全不同的照片:

    程序继续分配内存,直到抛出System.OutOfMemoryException。该示例演示如何利用Perfmon监视应用程序的状态。就像电视上的厨师一样,我作弊,并为这篇文章做了一个例子。在你的例子中,你可能不知道是什么导致了内存的广泛使用。该内存分析器来救援了!
    与任务管理器和Perfmon不同,内存分析器是帮助您找到内存问题或内存泄漏根本原因的工具。
    .NET内存探查器很好地集成到Visual Studio中,单击“新建探查器>启动内存探查器”菜单项可以分析应用程序的原因。从前面的示例中,我们可以看到与Perfmon类似的图片:

    这幅画看起来和以前很像。进程分配越来越多的内存(橙色和红色的线),进程抛出一个异常。在底部,显示从分析会话分配的所有对象,并对分配进行排序。查看顶行可以很好地指示是什么导致了泄漏。

    在这个简单的例子中,很明显,如果出现问题,字符串会添加到列表中。但是,大多数程序都比向列表中添加随机字符串复杂得多。这就是.NET内存探查器(以及其他工具)中可用的快照功能显示其优点的地方。快照就像Windows中的还原点,是当前内存使用情况的完整图片。通过在进程运行时单击“收集快照”按钮,可以获得差异:

    查看Live Instances>New列,很明显有人正在创建大量字符串。
    我不希望这是一个.NET内存分析器的广告,所以请查看他们的文档,了解如何在.NET程序中分析内存的完整图片。另外,一定要检查上述替代产品。他们都有免费的试用版,所以试试看,挑选你最喜欢的。
    我希望这篇文章为您提供了“一套非常特殊的技能”(抱歉)来帮助您调试内存问题。不幸的是,查找内存泄漏可能非常困难,需要一些培训和经验。

  • 相关阅读:
    一个漂亮的lazarus做的pagecontrol
    预测,阿里盒子必将失败!
    sex在软件开发中的运用--SIX技术
    糟糕的@@identity,SCOPE_IDENTITY ,IDENT_CURRENT
    Delphi、Lazarus保留字、关键字详解
    糟糕的界面设计
    Firebird存储过程--更加人性化的设计
    lazarus的动态方法和虚拟方法
    用户行为导向的交互设计
    Javascript的一个怪现象
  • 原文地址:https://www.cnblogs.com/yilang/p/12585884.html
Copyright © 2020-2023  润新知