C# 在';我做错了一切

C# 在';我做错了一切,c#,memory-leaks,C#,Memory Leaks,唉,我的程序某处有内存泄漏,但如果我知道它是什么,我会被诅咒的 它的工作是读入一堆~2MB的文件,进行一些解析和字符串替换,然后以各种格式输出它们。当然,这意味着有很多字符串,所以内存跟踪显示我有很多字符串,这正是我所期望的。程序的结构是一系列类(每个类都在自己的线程中,因为我是个白痴),它们作用于表示内存中每个文件的对象。(每个对象都有一个两端都使用锁的输入队列。这意味着我可以并行运行这个简单的处理,但也意味着我在内存中有多个2MB对象。)每个对象的结构由一个模式对象定义 我的处理类在完成处理

唉,我的程序某处有内存泄漏,但如果我知道它是什么,我会被诅咒的

它的工作是读入一堆~2MB的文件,进行一些解析和字符串替换,然后以各种格式输出它们。当然,这意味着有很多字符串,所以内存跟踪显示我有很多字符串,这正是我所期望的。程序的结构是一系列类(每个类都在自己的线程中,因为我是个白痴),它们作用于表示内存中每个文件的对象。(每个对象都有一个两端都使用锁的输入队列。这意味着我可以并行运行这个简单的处理,但也意味着我在内存中有多个2MB对象。)每个对象的结构由一个模式对象定义

我的处理类在完成处理后引发事件,并传递对包含我的所有字符串的大型对象的引用,以将其添加到下一个处理对象的队列中。将事件替换为要添加到队列的函数调用并不能阻止泄漏。其中一种输出格式要求我使用非托管对象。在类上实现Dispose()并不能阻止泄漏。我已经用索引名替换了对schema对象的所有引用。没有骰子。我不知道是什么原因,也不知道去哪里找。内存跟踪没有帮助,因为我看到的只是一堆正在创建的字符串,而我看不到引用在内存中的位置


在这一点上,我们很可能会放弃,但我有一种病态的需要知道我到底是怎么搞砸的。我知道堆栈溢出不能准确地梳理我的代码,但您可以建议什么策略来跟踪此泄漏?我可能会在我自己的时间里做这件事,所以任何方法都是可行的。

我会尝试的一种技术是系统地减少演示问题所需的代码量,而不会让问题消失。这被非正式地称为“分而治之”,是一种强大的调试技术。一旦你有了一个小例子来演示同样的问题,你就会更容易理解。也许此时记忆问题会变得更清楚。

只有一个人可以帮助你。那个人的名字是。(沉默)

但说真的。阅读她的博客(第一篇文章非常贴切)。看看她是如何调试这些东西的,会让你对你的问题有更深入的了解。

我喜欢微软的。它为可视化托管堆和跟踪泄漏提供了一些很好的工具

  • 将代码添加到 未经管理的对象在运行时要记录 ,并对唯一ID进行排序。 在创建对象时使用该唯一ID 再次被摧毁,你可以在 至少告诉他们哪一个要去 误入歧途
  • Grep为您选择的每个地方提供代码 构建新的对象;接着 代码路径以查看您是否有 匹配销毁
  • 将链接指针添加到 构造对象,因此您有一个 链接到构建的对象 在当前一个之前和之后。然后你可以稍后再扫描它们
  • 添加引用计数器
  • 是否有“调试malloc”可用

  • 托管调试外接程序(Strike之子)对于跟踪托管内存“泄漏”非常有用,因为根据定义,它们可以从gc根中发现

    它将在WinDbg或Visual studio中工作(尽管它在许多方面更易于在WinDbg中使用)

    这一点也不容易掌握。这是一个

    我也支持这个建议,去看看苔丝·费尔南德斯的博客。

    我使用档案器来追踪内存泄漏。它比方法论上的试验和错误更具确定性,结果也更快


    对于系统执行的任何操作,我都会拍摄一个快照,然后运行函数的一些迭代,然后再拍摄另一个快照。比较两者将显示在两者之间创建但未释放的所有对象。然后,您可以在创建堆栈帧时查看堆栈帧,从而确定哪些实例未被释放。

    您如何知道实际存在内存泄漏

    还有一件事:您编写的处理类正在使用事件。如果您注册了一个事件处理程序,它将使拥有该事件的对象保持活动状态,即GC无法收集它。如果要对对象进行垃圾收集,请确保取消注册所有事件处理程序。

    获取以下信息:


    内存和性能分析非常棒。能够看到正确的数字而不是猜测,这使得优化非常快。我在工作中经常使用它来减少主应用程序的内存占用。

    如果您的非托管对象确实是泄漏的原因,您可能希望它在分配非托管内存时调用
    AddMemoryPressure
    ,并在
    Finalize
    /
    Dispose
    /中调用
    removeMoryPressure
    。这将使GC更好地处理这种情况,因为它可能没有意识到需要安排收集,否则。

    请小心定义“泄漏”。“使用更多内存”甚至“使用过多内存”与“内存泄漏”不同。在垃圾收集环境中尤其如此。可能只是GC不需要收集您看到使用的额外内存。还要注意虚拟内存使用和物理内存使用之间的区别

    最后,并非所有的“内存泄漏”都是由“内存”类问题引起的。有一次我被告知(没有被要求)修复导致IIS频繁重启的紧急内存泄漏。事实上,我做了分析,发现我在StringBuilder类中使用了很多字符串。我为StringBuilder实现了一个对象池(来自MSDN文章),
    static unigned __int64 _foo_id = 0;
    foo::foo()
    {
        ++_foo_id;
        if (_foo_id == MAGIC_BAD_ALLOC_ID)
            DebugBreak();
        std::werr << L"foo::foo @ " << _foo_id << std::endl;
    }
    foo::~foo()
    {
        --_foo_id;
        std::werr << L"foo::~foo @ " << _foo_id << std::endl;
    }