有什么方法可以解决C#中的内存泄漏问题

我正在学习C#。 据我所知,您必须正确设置以使垃圾收集器实际删除所有内容。 我正在寻找多年来从你那里学到的智慧,聪明。

我来自C ++背景,非常习惯代码气味和开发模式。 我想知道C#中的代码气味是什么样的。 给我建议!

删除内容的最佳方法是什么?

当你遇到“内存泄漏”时,你怎么能弄明白?


编辑:我正在尝试开发一个“总是为内存管理做的事情”的列表


非常感谢。

在C#中,.NET Framework使用托管内存,所有内容(但分配的​​非托管资源)都是垃圾回收。

可以安全地假设托管类型总是被垃圾收集。 这包括arraysclassesstructures 。 随意做int[] stuff = new int[32]; 并忘记它。

如果在类中打开文件,数据库连接或任何其他非托管资源,请实现IDisposable接口,并在Dispose方法中取消分配非托管资源。

任何实现IDisposable的类都应该显式关闭,或者在(我认为很酷)使用块中使用;

 using (StreamReader reader = new StreamReader("myfile.txt")) { ... your code here } 

这里.NET将在{}范围之外处置读取器。

GC的第一件事是它是非确定性的; 如果你想及时清理资源,实现IDisposable并使用; 这不会收集托管内存,但可以帮助很多非托管资源和转发链。

特别需要注意的事项:

  • 大量固定(对GC的作用有很多限制)
  • 很多终结器(你通常不需要它们;减慢GC)
  • 静态事件 – 保持大量对象图存活的简单方法;-p
  • 廉价的长寿命物体上的事件,可以看到应该清理的昂贵物体
  • “捕获变量”意外地保持图表存活

为了调查内存泄漏……“SOS”是最简单的路线之一; 您可以使用SOS查找某个类型的所有实例,以及可以看到它的内容等。

一般来说,你越少担心C#中的内存分配,你就越好。 当我遇到收集问题时,我会把它留给分析师告诉我。

您不能像在C ++中那样在C#中创建内存泄漏。 垃圾收集器总是“有你的后背”。 你可以做的是创建对象并保持对它们的引用,即使你从不使用它们。 这是一个需要注意的代码味道。

除此之外:

  • 了解收集频率的概念(出于性能原因)
  • 不要持有超出需要的对象引用
  • 一旦完成它们就处理实现IDisposable的对象(使用using语法)
  • 正确实现IDisposable接口

我能想到的内存泄漏的主要来源是:

  • 保持对你不再需要的对象的引用(通常在某种集合中)所以在这里你需要记住,你添加到你已经引用的集合中的所有东西都将保留在内存中。

  • 有循环引用,例如让代表在事件中注册。 因此,即使您明确地不引用对象,也无法收集垃圾,因为其中一个方法已注册为具有事件的委托。 在这些情况下,您需要记住在丢弃引用之前删除委托。

  • 与本机代码互操作并且无法释放它。 即使您使用实现终结器的托管包装器,CLR也不会足够快地清理它们,因为它不了解内存占用。 您应该使用using(IDisposable){}模式

内存管理需要考虑的另一个问题是,如果要实现任何Observer模式而不是正确处理引用。

例如:对象A监视对象B对象B如果从A到B的引用未被处置属性,则GC将被处置,GC将不会处置该对象。 因为仍然分配事件处理程序GC不会将其视为未使用的资源。

如果您正在使用一小组对象,这可能与我无关。 但是,如果您使用数千个对象,这可能会导致应用程序生命周期内的内存逐渐增加。

有一些很棒的内存管理软件应用程序可以监视应用程序堆的运行情况。 我发现使用.Net Memory Profiler可以获得很大的好处。

HTH

我建议使用.NET Memory Profiler

.NET Memory Profiler是一个function强大的工具,用于查找内存泄漏并优化用C#,VB.NET或任何其他.NET语言编写的程序中的内存使用情况。

.NET Memory Profiler将帮助您:

  • 查看实时内存和资源信息
  • 通过收集和比较.NET内存的快照轻松识别内存泄漏
  • 查找未正确处理的实例
  • 获取有关非托管资源使用情况的详细信息
  • 优化内存使用情况
  • 调查生产代码中的内存问题
  • 执行自动内存测试
  • 检索有关本机内存的信息

看看他们的video教程:

http://memprofiler.com/tutorials/

其他人已经提到了IDisposable的重要性,以及代码中需要注意的一些事项。

我想建议一些额外的资源; 在了解.NET GC的详细信息以及如何解决.NET应用程序中的内存问题时,我发现以下内容非常宝贵。

CLR通过C# by Jeffrey Richter是一本很好的书。 值得购买的价格只是关于GC和内存的章节。

此博客 (由Microsoft“ASP.NET升级工程师”)通常是我使用WinDbg,SOS以及发现某些类型的内存泄漏的提示和技巧的首选来源。 Tess甚至设计了.NET调试演示/实验室,它将引导您解决常见的内存问题以及如何识别和解决它们。

适用于Windows的调试工具 (WinDbg,SOS等)

您可以使用像CLR profiler这样的工具,它需要一些时间来学习如何正确使用它,但毕竟它是免费的。 (它帮我几次找到我的内存泄漏)

确保对象被删除的最佳方法,或者在.NET语言中,垃圾收集,是确保所有根引用(可以通过方法和对象跟踪线程的调用堆栈上的第一个方法的引用)到对象设置为null。

如果对象有任何有根引用,则无论是否实现IDisposable,GC都不会,也不会收集对象。

循环引用没有惩罚或内存泄漏的可能性,因为GC标记它在对象图中访问了哪些对象。 对于委托或事件处理程序,可能常常忘记将事件中的引用移除到目标方法,以便在事件被植根时无法收集包含目标方法的对象。

删除内容的最佳方法是什么?

注意:以下内容仅适用于包含非托管资源的类型。 它对纯托管类型没有帮助。

可能最好的方法是实现并遵循IDisposable模式; 并在实现它的所有对象上调用dispose方法。

‘使用’声明是你最好的朋友。 松散地说,它会在实现IDisposable的对象上调用dispose。