我的程序,唉,某处有内存泄漏,但如果我知道它是什么,我会被诅咒。
它的工作是读取一堆~2MB的文件,进行一些解析和字符串替换,然后以各种格式输出它们。当然,这意味着很多字符串,因此进行内存跟踪表明我有很多字符串,这正是我所期望的。程序的结构是一系列类(每个都在它们自己的线程中,因为我是 idiot ),它们作用于代表内存中每个文件的对象。 (每个对象都有一个输入队列,两端都使用锁。虽然这意味着我可以并行运行这个简单的处理,但这也意味着我有多个2MB对象位于内存中。)每个对象的结构都是由一个模式对象定义的。
我的处理类在完成处理时引发事件,并传递对包含所有字符串的大对象的引用,以将其添加到下一个处理对象的队列中。使用函数调用替换事件以添加到队列不会阻止泄漏。其中一种输出格式要求我使用非托管对象。在类上实现Dispose()不会阻止泄漏。我已使用索引名称替换了对架构对象的所有引用。没有骰子。我不知道是什么导致它,不知道在哪里看。内存跟踪没有帮助,因为我看到的是一堆正在创建的字符串,我没有看到引用在内存中的位置。
我们现在几乎要放弃并回滚,但我有一个病态的需要,确切地知道我是如何弄乱它的。我知道Stack Overflow无法完全梳理我的代码,但您可以建议哪些策略来跟踪此泄漏情况?我可能会在自己的时间里这样做,所以任何方法都是可行的。
答案 0 :(得分:11)
我尝试的一种技术是系统地减少演示问题所需的代码量,而不会使问题消失。这被非正式地称为“分而治之”,是一种强大的调试技术。一旦你有一个演示同样问题的 small 示例,你就会更容易理解。也许记忆问题会在那时变得更加清晰。
答案 1 :(得分:5)
只有一个人可以帮助你。那个人的名字是Tess Ferrandez。 (沉默)
但是说真的。阅读她的博客(第一篇文章非常贴切)。看看她如何调试这些东西,可以让你深入了解你的问题是什么。
答案 2 :(得分:2)
我喜欢微软的CLR Profiler。它提供了一些很好的工具,用于可视化托管堆和跟踪泄漏。
答案 3 :(得分:1)
我使用dotTrace分析器来跟踪内存泄漏。它比方法论的试验和错误更具确定性,并且可以更快地提高结果。
对于系统执行的任何操作,我拍摄快照然后运行该函数的几次迭代,然后拍摄另一个快照。比较两者将显示在两者之间创建但未释放的所有对象。然后,您可以在创建时查看堆栈框架,从而确定哪些实例未被释放。
答案 4 :(得分:1)
获取此信息:http://www.red-gate.com/Products/ants_profiler/index.htm
内存和性能分析非常棒。能够实际看到正确的数字而不是猜测使得优化非常快。我已经在工作中使用它来减少主应用程序的内存占用。
答案 5 :(得分:0)
答案 6 :(得分:0)
管理调试添加SoS(罢工之子)非常强大,能够追踪管理内存的“漏洞”,因为根据定义可以从gc根中找到它们。
它可以在WinDbg或Visual Studio中工作(虽然它在WinDbg中很容易使用)
掌握这一点并不容易。这是tutorial
我也会推荐查看Tess Fernandez的博客。
答案 7 :(得分:0)
你怎么知道你实际上有内存泄漏的事实?
另一件事:您写道您的处理类正在使用事件。如果您已经注册了一个事件处理程序,它将使拥有该事件的对象保持活动状态 - 即GC无法收集它。如果您希望对象被垃圾收集,请确保取消注册所有事件处理程序。
答案 8 :(得分:0)
如果您的非托管对象确实是泄漏的原因,您可能希望在AddMemoryPressure
/ RemoveMemoryPressure
分配非托管内存和Finalize
时调用Dispose
/它在哪里释放非托管内存。这将使GC更好地处理这种情况,因为它可能没有意识到需要安排收集。
答案 9 :(得分:0)
小心如何定义“泄漏”。 “使用更多内存”甚至“使用太多内存”与“内存泄漏”不同。在垃圾收集环境中尤其如此。可能只是GC不需要收集你看到的额外内存。还要注意虚拟内存使用和物理内存使用之间的区别。
最后并非所有“内存泄漏”都是由“内存”类型的问题引起的。我曾被告知(未被要求)修复导致IIS频繁重启的紧急内存泄漏。事实上,我进行了分析,发现我在StringBuilder类中使用了很多字符串。我为StringBuilders实现了一个对象池(来自MSDN文章),并且内存使用量大幅下降。
IIS仍然经常重启。这是因为没有内存泄漏。相反,有非托管代码声称是线程安全但不是。在Web服务(多个线程)中使用它会导致它在整个C运行时库堆中写入。由于没有人在寻找非托管异常,所以没有人看到这一点,直到我碰巧使用自动化QA对AQtime进行了一些分析。碰巧有一个事件窗口,恰好显示了来自C运行时库的痛苦声。
锁定了对非托管代码的调用,“内存泄漏”消失了。
答案 10 :(得分:0)
您提到过您使用的活动。当您完成对象时,是否从这些事件中删除处理程序?我发现,如果你添加了一堆处理程序而没有删除它们,那么'松散'事件处理程序会导致很多内存泄漏问题。
答案 11 :(得分:0)
.Net的最佳内存分析工具是:
此外,虽然我在这里,但.Net的最佳性能分析器是:
http://www.yourkit.com/dotnet/download/index.jsp
它们也物有所值,开销低且易于使用。任何认真对待.Net开发的人都应该考虑将这两者作为个人投资并立即购买。他们都有免费试用。
我使用C#编写了超过70万行代码的实时游戏引擎,并使用这两种工具花费了数百小时。我从2002年开始使用Sci Tech产品和YourKit!过去三年。虽然我已经尝试了其他一些我总是回到这些。
恕我直言,他们都非常出色。答案 12 :(得分:0)
与Charlie Martin相似,您可以这样做:
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;
}
如果你可以重新创建它,即使是一次或两次具有相同的分配ID,这也可以让你看看当时和那里发生了什么(显然TLS /线程也必须处理,如果需要,但我离开了为了清楚起见)。