跟踪.NET Windows服务内存泄漏

时间:2017-04-05 00:55:31

标签: c# memory-leaks

在生产中安装我的Windows服务之前,我一直在寻找可以执行的可靠测试,以确保我的代码不会包含内存泄漏。 但是,我在网上找到的所有内容都是使用任务管理器查看已用内存或一些付费内存分析器工具。

根据我的理解,查看任务管理器并不是很有用,也无法确认内存泄漏(如果有的话)。

  1. 如何确认是否有内存泄漏?

  2. 是否有任何免费工具可以找到内存泄漏的来源?

  3. 注意:我正在使用.Net Framework 4.6和Visual Studio 2015社区

7 个答案:

答案 0 :(得分:8)

你可以使用任务管理器。 GC应用程序可能会泄漏内存,它会显示在那里。

但是...

免费工具 - " .Net CLR分析器"

有一个免费工具,它来自微软,它真棒。这是泄漏引用的所有程序必须使用的。搜索MS'站点。

泄漏引用意味着您忘记将对象引用设置为null,或者它们永远不会留下范围,这几乎与垃圾收集的语言一样不会发生 - 列表构建和不清除,指向委托的事件处理程序等

它相当于内存泄漏的GC,并且具有相同的结果。这个程序告诉你什么参考文件占用了大量的内存 - 你会知道它是否应该是这样的,如果没有,你可以找到它们并解决问题!

它甚至可以清晰地显示哪些对象分配了什么内存(因此您可以追踪错误)。如果你需要解释的话,我相信你有很多想法。

Memory Usage Visualization

Wikipedia page with download links...

注意:您可能必须运行您的应用而非作为服务才能使用此功能。它首先启动,然后运行您的应用程序。您可以使用TopShelf执行此操作,也可以将内容放入从EXE运行的dll中,该EXE支持服务集成(服务主机模式)。

答案 1 :(得分:3)

虽然托管代码不代表直接内存管理,但您仍需要管理实例。这些实例声称'记忆。这些都与这些实例的使用有关,当你不期望它们存在时,它们会保持活着。

只是众多例子中的一个:错误使用一次性类会导致许多实例声称内存。对于Windows服务,实例的缓慢但稳定的增加最终会导致大量内存使用。

是的,有一个分析内存泄漏的工具。它不是免费的。但是,您可以在7天的路径中识别您的问题。

我建议你在.NET Memory Profiler抓一个战利品。

在开发过程中分析内存泄漏非常棒。它使用快照的概念来比较新实例,处置实例等。这有助于理解您的服务如何使用其内存。然后,您可以深入了解新实例创建或保持活动的原因。

是的,您可以进行测试以确认是否引入了内存泄漏。 但是,开箱即用,这将不是非常有用。这是因为没有人能预料到运行时会发生什么。该工具可以分析您的应用程序是否存在常见问题,但这不能保证。

但是,您可以使用此工具将内存消耗集成到单元测试框架中,例如NUnitMSTest

答案 2 :(得分:2)

当然,内存分析器是第一种尝试的工具,但它只会告诉您实例是否在不断增加。你仍然想知道它们是否正在增加是否正常。此外,一旦你确定某些实例在没有充分理由的情况下继续增加(意思是,你有泄漏),你将需要确切地知道哪些调用树导致它们的分配,以便你可以解决分配它们的代码和修复它以便最终释放它们。

以下是我多年来在处理此类问题时收集的一些知识:

  1. 尽可能将您的服务作为常规可执行文件进行测试。试图将服务作为实际服务进行测试只会让事情变得太复杂。

  2. 养成明确撤消在你正在做的事情范围的最后所做的一切的习惯。例如,如果您将观察者注册到某个观察者的事件中,那么应该总是在某个时间点(处理观察者或观察者?)取消注册它。从理论上讲,垃圾收集应该通过收集相互连接的观察者和观察者的整个图表来处理这个问题,但在实践中,如果你不习惯忘记撤消你所做的事情,就会导致内存泄漏。

  3. 尽可能使用IDisposable,如果有人忘记调用Dispose(),请让您的析构函数报告。有关此方法的更多信息,请访问:Mandatory disposal vs. the "Dispose-disposing" abomination披露:我是该文章的作者。

  4. 在程序中定期检查点,释放应该可释放的所有内容(就好像程序正在执行有序关闭以便终止),然后强制进行垃圾收集以查看是否有任何泄漏。< / p>

  5. 如果某些类的实例似乎在泄漏,请使用以下技巧来发现导致其分配的精确调用树:在该类的构造函数中,分配异常对象而不抛出它,获取堆栈跟踪异常,并存储它。如果您稍后发现此对象已泄露,则您具有必要的堆栈跟踪。只是不要使用太多的对象,因为分配异常并从中获取堆栈跟踪是非常慢的,只有Microsoft知道原因。

答案 3 :(得分:1)

您可以尝试免费的Memoscope内存分析器

https://github.com/fremag/MemoScope.Net

我不同意您可以信任任务管理器来检查您是否有内存泄漏。垃圾收集器的问题在于它可以根据启发式来决定在内存峰值后保留内存并且不将其返回到操作系统。您可能拥有2 GB的提交大小,但其中90%可以免费。

您应该使用VMMAP在测试期间检查您的进程包含哪种类型的内存。您不仅拥有托管堆,还拥有非托管堆,专用字节,堆栈(线程泄漏),共享文件以及需要跟踪的更多内容。

VMMap还有命令行界面,可以定期创建快照,您可以稍后检查。如果你有内存增长,你可以找出泄漏的内存类型,这取决于不同的调试工具接近的泄漏类型。

答案 4 :(得分:0)

我不会说垃圾收集器是绝对可靠的。有时它会在不知不觉中失败并且它们不那么直接。内存流是内存泄漏的常见原因。您可以在一个上下文中打开它们,它们甚至可能永远不会被关闭,即使用法包含在using语句中(一次性对象的定义应在其使用超出范围后立即清除)。如果由于内存不足而遇到崩溃,Windows会创建可以筛选的转储文件。

enter link description here

这绝不是有趣或轻松的,而且相当繁琐,但它往往是你最好的选择。

易于创建内存泄漏的常见区域是使用System.Drawing dll,内存流以及如果您正在进行一些严重的多线程的任何事情。

答案 5 :(得分:0)

如果您使用Entity Framework和DI模式,也许使用Castle Windsor,您可以轻松地获得内存泄漏。

要做的主要事情是使用using(){}语句,您可以在其中自动将对象标记为已处置。

此外,您希望在只读取而不是写入的实体框架上关闭自动跟踪。最好隔离您的写入,此时使用using(){},获取dbContext(跟踪),编写数据。

如果你想调查堆上的内容。我使用过的最好的工具是RedGate ANTS http://www.red-gate.com/products/dotnet-development/ants-memory-profiler/solving-memory-problems/getting-started并不便宜,但它确实有效。

然而,通过使用using(){}模式,你可以(不要制作静态或单独的DbContext,并且在大量的更新循环中永远不会有一个上下文,请像往常一样经常处理它们可以!)然后你发现记忆通常不是问题。

希望这有帮助。

答案 6 :(得分:-1)

除非您处理非托管代码,否则我会非常大胆地说您不必担心内存泄漏。托管代码中的任何未引用的对象都将被垃圾收集器删除,并且在.net框架内找到内存泄漏的可能性我会说你应该被认为是非常幸运的(好吧,不幸)。你不必担心内存泄漏。

但是,如果永远不会释放对象的引用,您仍会遇到不断增长的内存使用量。例如,假设您保留了内部日志结构,并且只是将条目添加到日志列表中。然后每个条目仍然有来自日志列表的引用,因此永远不会被收集。

根据我的经验,您绝对可以使用任务管理器来指示您的系统是否存在日益严重的问题;如果内存使用率稳步上升,你知道你有问题。如果它增长到某一点但最终会收敛到一定的大小,则表明它已达到其操作阈值。

如果您想要更详细地了解托管内存使用情况,可以下载由Microsoft开发的进程资源管理器here。它仍然相当直率,但它提供了比任务管理器更好的统计视图。