我正在学习C#。据我所知,您必须正确设置以使垃圾收集器实际删除所有内容。我正在寻找你多年来从智慧中学到的智慧。
我来自C ++背景,并且非常习惯代码气味和开发模式。我想知道C#中的代码气味是什么样的。给我建议!
删除内容的最佳方法是什么?
如何解决“内存泄漏”的问题?
编辑:我正在尝试开发一个“总是为内存管理做的事情”的打卡列表
非常感谢。
答案 0 :(得分:18)
C#,.NET Framework使用托管内存,所有内容(但分配的非托管资源)都是垃圾回收。
可以安全地假设托管类型总是被垃圾收集。其中包括arrays
,classes
和structures
。随意做int[] stuff = new int[32];
并忘掉它。
如果在类中打开文件,数据库连接或任何其他非托管资源,请实现IDisposable接口,并在Dispose方法中取消分配非托管资源。
任何实现IDisposable的类都应该显式关闭,或者在(我认为很酷)使用块中使用;
using (StreamReader reader = new StreamReader("myfile.txt"))
{
... your code here
}
这里.NET将在{}范围之外处理读者。
答案 1 :(得分:11)
GC的第一件事是它是非确定性的;如果您希望及时清理资源,请实施IDisposable
并使用using
;它不会收集托管内存,但可以帮助很多非托管资源和转发链。
特别需要注意的事项:
调查内存泄漏......“SOS”是最简单的路线之一;您可以使用SOS查找某个类型的所有实例,以及可以看到它的内容等。
答案 2 :(得分:3)
一般来说,你越少担心C#中的内存分配,你就越好。我会把它留给一个分析器告诉我什么时候我有收集问题。
您无法像在C ++中那样在C#中创建内存泄漏。垃圾收集器总是“有你的后背”。你可以做的是创建对象并保持对它们的引用,即使你从不使用它们。这是一个需要注意的代码味道。
除此之外:
using
语法)答案 3 :(得分:3)
我能想到的内存泄漏的主要来源是:
保持对不再需要的对象的引用(通常在某种集合中)所以在这里你需要记住,你添加到你所引用的集合中的所有东西都将保留在内存中。
有循环引用,例如让代表在活动中注册。因此,即使您明确地不引用对象,也无法收集垃圾,因为其中一个方法已注册为具有事件的委托。在这些情况下,您需要记住在丢弃引用之前删除委托。
答案 4 :(得分:1)
内存管理需要考虑的另一件事是,如果要实现任何Observer模式而不是正确处理引用。
例如: 对象A监视对象B. 如果从A到B的参考没有处置属性,则GC将被丢弃,GC将不会合意地处置该对象。因为仍然分配事件处理程序GC不会将其视为未使用的资源。
如果您正在使用一小组对象,这可能与我无关。但是,如果您使用数千个对象,这可能会导致应用程序生命周期内的内存逐渐增加。
有一些很棒的内存管理软件应用程序可以监视应用程序堆的内容。我发现使用.Net Memory Profiler可以带来很大的好处。
HTH
答案 5 :(得分:1)
我建议使用 .NET Memory Profiler
.NET Memory Profiler是一个功能强大的工具,用于查找内存泄漏并优化用C#,VB.NET或任何其他.NET语言编写的程序中的内存使用情况。
.NET Memory Profiler将帮助您:
看看他们的视频教程:
答案 6 :(得分:1)
其他人已经提到了IDisposable的重要性,以及代码中需要注意的一些事项。
我想建议一些额外的资源;在了解.NET GC的详细信息以及如何解决.NET应用程序中的内存问题时,我发现以下内容非常宝贵。
杰弗里里希特的CLR via C#是一本优秀的书。值得购买的价格只是关于GC和内存的章节。
此blog(由Microsoft“ASP.NET升级工程师”)通常是我使用WinDbg,SOS以及发现某些类型的内存泄漏的提示和技巧的首选来源。 Tess甚至设计了.NET调试演示/实验室,它将引导您解决常见的内存问题以及如何识别和解决它们。
Debugging Tools for Windows(WinDbg,SOS等)
答案 7 :(得分:0)
删除内容的最佳方法是什么?
注意:以下内容仅适用于包含非托管资源的类型。它对纯托管类型没有帮助。
可能最好的方法是实现并遵循IDisposable模式;并在实现它的所有对象上调用dispose方法。
'使用'声明是你最好的朋友。松散地说,它会在实现IDisposable的对象上调用dispose。
答案 8 :(得分:0)
您可以使用像CLR profiler这样的工具,这需要一些时间来学习如何正确使用它,但毕竟它是免费的。 (它帮我找了几次内存泄漏)
答案 9 :(得分:0)
确保删除对象或在.NET术语中进行垃圾收集的最佳方法是确保所有根引用(可以通过方法和对象跟踪线程调用堆栈上第一个方法的引用)对象设置为null。
如果对象有任何有根引用,GC不能也不会收集对象,无论它是否实现了IDisposable。
循环引用没有惩罚或内存泄漏的可能性,因为GC标记它在对象图中访问了哪些对象。对于委托或事件处理程序,可能常常忘记将事件中的引用移除到目标方法,以便在事件被植根时无法收集包含目标方法的对象。