.net内存优化好习惯 返回

C#论坛 老数据
2261

image.png


大型.net应用程序中的内存问题是一种无声的杀手。有点像高血压。你可以长时间吃垃圾食品,直到有一天你面临一个严重的问题。在.net程序的情况下,严重的问题可能是高内存消耗、主要的性能问题和彻底的崩溃。在这篇文章中,您将看到如何将我们的应用程序的内存保持在健康水平。

如何知道您的内存使用是否正常?你需要做些什么来保持健康呢?这正是本文要讨论的内容。我们将回顾6个需要保留的最佳实践


1. 尽快回收对象

为了使程序快速工作,主要目标是尽快回收对象。要理解为什么它很重要,您需要理解. net的分代垃圾收集器。当使用new子句创建对象时,它们是在第0代的堆上创建的。这是一个很小的内存空间。如果它们在有第0代集合时仍然被引用,它们将被提升到第1代。第1代是更大的内存空间。如果在有第1代集合时它们仍然被引用,它们将被提升到第2代。

第0代收集是最常见的,而且非常快。

2. 使用缓存但是要小心

像缓存这样的机制从定义上来说是很麻烦的。这些是长寿命的临时对象,可能会被提升到第2代。虽然这不利于GC压力,但通常是值得的,因为缓存确实有助于提高性能。但你得小心点。

缓解内存压力的一种方法是使用可变缓存对象。这意味着您将更新现有的对象,而不是替换缓存对象。这将意味着GC提升对象和启动更多第0代和第1代收集的工作量更少。

这是一个例子。让我们假设您正在从您的在线杂货店缓存库存物品。您有一个缓存机制,用于存储经常查询的项目的价格和数据。比如那些会导致高血压的冷冻披萨。假设每5分钟您就必须使缓存失效并重新查询数据库,以防细节发生变化。因此,在本例中,您将改变现有对象的状态,而不是创建一个新的Pizza对象。

3. 查看内存百分比

如果您想知道垃圾收集对执行时间的影响有多大,这是很容易做到的。查看性能计数器。net CLR内存| %时间在GC。这将显示垃圾收集器使用了执行时间的百分比。有许多工具可以查看性能计数器。在Windows中,可以使用PerfMon。在Linux中可以使用dotnet-trace。要了解更多信息,请查看我的文章《使用。net中的性能计数器来测量内存、CPU和所有东西》。


我将给你们一些神奇的数字但不要轻信因为每件事都有它自己的背景。对于大型应用程序,10%的GC时间可能是一个正常的百分比。20%的GC时间是临界时间,超过20%就意味着你有问题了。

4. 注意2代收藏

除了GC中的%时间之外,您应该监视的另一个大指标是第2代收集的数量。或者更确切地说是第2代的收集率。我们的目标是让它们尽可能少。考虑到这些都是全内存堆集合。当GC收集所有内容时,它们有效地冻结了应用程序的所有线程。


我不能给出一个神奇的数字,你应该有多少第2代收藏。但我建议每隔一段时间就积极监控这个数字,如果比率上升,那么你可能会增加一些非常糟糕的行为。你可以通过性能计数器。net CLR Memory | % Gen 2 Collections看到这个数字

PerfMon showing Gen 2 collections

5. 监控稳定的内存消耗

考虑应用程序的常规状态。有些事情总是会发生的。它可能是服务请求的服务器、从队列中提取消息的服务、有很多屏幕的桌面应用程序。在此期间,应用程序不断创建新的对象,执行一些操作,然后释放这些对象并返回到正常状态。这意味着从长远来看,内存消耗应该大致相同。当然,它可能会在高峰时间或繁重的操作期间达到高水平,但一旦完成,它应该会恢复正常。

但是如果您监视了很多应用程序,您可能知道内存有时会随着时间的推移而增加。平均消费缓慢上升到更高的水平,尽管它在逻辑上不应该。产生这种行为的原因几乎总是内存泄漏。这是一种现象,对象不再被使用,但由于某种原因,它仍然被引用,因此从未被收集。

当一个操作导致对象泄漏时,每个这样的操作都会消耗更多的内存。随着时间的推移,记忆升起。当足够长的时间过去,记忆接近它的极限。在32位进程中,这个限制是4GB。在64位进程中,它取决于机器的约束。当我们如此接近极限时,垃圾收集器就会恐慌。它开始为每个其他分配触发全内存第2代收集,以避免耗尽内存。这很容易使您的应用程序变得缓慢。当更长的时间过去时,内存确实达到了其极限,应用程序崩溃,出现灾难性的OutOfMemoryException。这就相当于心脏病发作。

为了确保您不会达到这种状态,我的建议是积极地监控一段时间内的内存消耗。最好的方法是查看性能计数器Process | Private Bytes。您可以使用Process explorer或PerfMon轻松实现这一点。

6. 定期查找内存泄漏

毫无疑问,内存问题的头号罪魁祸首是内存泄漏。它们很容易造成伤害,它们可以被忽视很长一段时间,最终造成大量的伤害。在应用程序始终崩溃的阶段修复内存泄漏是非常困难的。你必须改变旧的代码,这可能会导致各种各样的回归bug。因此,我将为具有正常内存的应用程序添加第二个主要目标:修复和避免内存泄漏。

期望您的团队永远不引入内存泄漏是不现实的。而且,在整个应用程序中每次进行新的提交时都检查内存泄漏是不实际的。相反,我建议添加每隔一段时间检查内存泄漏的实践。可以是每周、每月或每季度。只要对你有用就行。

解决这个问题的一种方法是在每次看到内存上升时检查内存泄漏(如技巧#5中建议的那样)。但问题是内存占用较少的泄漏也会导致很多问题。例如,您可能有一些应该被收集的对象,但它们仍然是活动的,并且仍然有代码在其中执行,这将导致不正确的行为。

检测和修复内存泄漏的最佳方法是使用内存分析器。请在我的文章《揭开c# . net中内存分析器的神秘面纱:第2部分:内存泄漏》中了解如何做到这一点。

要了解哪种设计模式会导致内存泄漏,请查看我的文章《。net中导致内存泄漏的8种方式》。


总结:

所以你知道了,这是一个健康记忆状态的食谱。如果您遵循这些建议,您的应用程序将会很快并且消耗很少的内存。但是认真地说,请吃健康的食物和锻炼


热忱回答0