您当前的位置:首页 > 电脑百科 > 程序开发 > 语言 > .NET

保持 .NET 应用程序内存健康的 6 个最佳实践

时间:2021-07-15 11:57:29  来源:  作者:IT狂人日记

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

你怎么知道你的内存使用情况是否健康?你需要做什么来保持它的健康?这正是本文要讨论的内容。我们将介绍 6 种最佳做法,以保持内存健康并在出现问题时检测问题。您还将看到优化垃圾收集并使您的应用程序非常快速的最佳实践。

1. 应尽快收集对象

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

Gen 0 集合是最频繁的并且非常快。Gen 1 集合涵盖 Gen 0 内存空间和 Gen 1 内存空间,并且它们更昂贵。Gen 2 集合包括整个内存空间,包括大对象堆 (LOH)。它们非常昂贵。GC 已优化为具有许多 Gen 0 集合、较少的 Gen 1 集合和很少的 Gen 2 集合。但是,如果您有许多对象被提升到更高代,那么您将产生相反的效果。这会导致内存压力[1](又名 GC 压力)和性能不佳。

顺便说一下,新对象的分配非常便宜。您唯一需要担心的是集合。

那么如何在低代收集对象呢?很简单,只需确保不会尽快引用它们即可。有些对象,比如单例,必须永远在内存中。没关系,它们通常是不会消耗大量内存的服务。

2. 使用缓存……但要小心

根据定义,像缓存这样的机制很麻烦。这些是长期存在的临时对象,可能会升级到第 2 代。虽然这对 GC 压力不利,但通常值得付出代价,因为缓存确实可以帮助提高性能。但你必须密切关注它。

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

这是一个例子。假设您正在缓存来自在线杂货店的库存商品。您有一个缓存机制来存储经常查询的项目的价格和数据。就像那些会导致高血压的冷冻比萨饼。假设每 5 分钟您必须使缓存无效并重新查询数据库,以防细节发生变化。因此,在这种情况下,不是创建新Pizza对象,而是更改现有对象的状态。

3. 留意 GC 中的时间百分比

如果您想知道垃圾收集对执行时间的影响有多大,这很容易做到。简单看看性能计数器.NET CLR Memory | % GC 时间。这将显示垃圾收集器使用了百分之多少的执行时间。有许多工具可以查看性能计数器。在 windows 中,您可以使用 PerfMon。在 linux 中,您可以使用dotnet-trace[2]。要了解更多信息,请查看我的文章Use Performance Counters in .NET to measure Memory, CPU, and Everything[3]。

我将给您一些神奇的数字,但请注意这些数字,因为一切都有其自身的背景。对于大型应用程序,10% 的 GC 时间可能是一个健康的百分比。GC 中 20% 的时间处于临界状态,任何更多都意味着您有问题。

4. 留意那些 Gen 2 Collections

除了 GC 中的时间百分比,您应该监控的另一个重要指标是 Gen 2 收集的数量。或者更确切地说是第 2 代收藏的速度。目标是尽可能少地使用它们。考虑到这些是完整的内存堆集合。当 GC 收集所有内容时,它们有效地冻结了应用程序的所有线程。

对于您应该拥有多少 Gen 2 系列,我无法给出一个神奇的数字。但我建议每隔一段时间积极监控这个数字,如果比率上升,那么你可能会添加一些非常糟糕的行为。您可以通过性能计数器.NET CLR Memory |查看该数字。% Gen 2 集合

保持 .NET 应用程序内存健康的 6 个最佳实践

 

PerfMon 显示第 2 代集合

 

5. 监控稳定的内存消耗

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

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

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

为了确保您不会达到这种状态,我的建议是随着时间的推移主动监控内存消耗。最好的方法是查看性能计数器Process | Private Bytes。您可以使用Process explorer[5]或 PerfMon轻松完成。

6. 定期查找内存泄漏

内存问题的#1 罪魁祸首毫无疑问是内存泄漏。很容易造成它们,它们可以被长期忽视,最终会造成大量损害。在应用程序持续崩溃的阶段修复内存泄漏非常困难。您必须更改可能导致各种回归错误的旧代码。因此,我将为具有健康内存的应用程序添加第二个主要目标:修复并避免内存泄漏。

期望您的团队永远不会引入内存泄漏是不现实的。并且在每次新提交时检查整个应用程序中的内存泄漏是不切实际的。相反,我建议添加每隔一段时间检查内存泄漏的做法,它可能是每周、每月或每季度,具体取决于你的需求。

一种方法是在每次看到内存上升时检查内存泄漏(如提示 #5 中建议的那样)。但问题是内存占用低的泄漏也会导致很多问题。例如,您可能有一些本应收集但仍处于活动状态的对象,并且仍有代码在其中执行,这会导致不正确的行为。

检测和修复内存泄漏的最佳方法是使用内存分析器。在我的文章Demystifying Memory Profilers in C# .NET Part 2: Memory Leaks 中[6]了解如何做到这一点。

要了解哪种设计会导致内存泄漏,请查看我的文章.NET 中可能导致内存泄漏的 8 种方式[7]。

总结

所以你有它,一个健康记忆状态的秘诀。如果您遵循这些建议,您的应用程序将很快并且消耗很少的内存。但说真的,请吃健康的食物和锻炼

References

[1] 内存压力: https://michaelscodingspot.com/avoid-gc-pressure/[2] dotnet-trace: https://github.com/dotnet/diagnostics/blob/master/documentation/dotnet-trace-instructions.md[3] Use Performance Counters in .NET to measure Memory, CPU, and Everything: https://michaelscodingspot.com/performance-counters/[4] 内存泄漏: https://michaelscodingspot.com/ways-to-cause-memory-leaks-in-dotnet/[5] Process explorer: https://docs.microsoft.com/en-us/sysinternals/downloads/process-explorer[6] Demystifying Memory Profilers in C# .NET Part 2: Memory Leaks 中: https://michaelscodingspot.com/memory-profilers-for-memory-leaks/[7] .NET 中可能导致内存泄漏的 8 种方式: https://michaelscodingspot.com/ways-to-cause-memory-leaks-in-dotnet/



Tags:.NET   点击:()  评论:()
声明:本站部分内容及图片来自互联网,转载是出于传递更多信息之目的,内容观点仅代表作者本人,如有任何标注错误或版权侵犯请与我们联系(Email:2595517585@qq.com),我们将及时更正、删除,谢谢。
▌相关推荐
编译和反编译.NET 中的编译是把开发人员写的 C# 代码转化为计算机可理解的代码的过程,也就是中间语言代码(IL代码)。在这个过程中,C# 源代码被转换为可执行文件(exe或者dll 文件)...【详细内容】
2022-07-15  Tags: .NET  点击:(1)  评论:(0)  加入收藏
我们在开发 webapi 项目时如果遇到 api 接口需要同时支持多个版本的时候,比如接口修改了入参之后但是又希望支持老版本的前端(这里的前端可能是网页,可能是app,小程序 等等)进行...【详细内容】
2022-07-14  Tags: .NET  点击:(3)  评论:(0)  加入收藏
Asp.Net Core Identity 是.Net自带的身份认证系统,支持用户界面 (UI) 登录功能,并且管理用户、密码、配置文件数据、角色、声明、令牌、电子邮件确认等等。使用Visual Studio...【详细内容】
2022-06-05  Tags: .NET  点击:(35)  评论:(0)  加入收藏
安装Hangfire新建ASP.NET Core空 项目,.Net Core版本3.1 往*.csproj添加包引用,添加新的PackageReference标记。如下所示。请注意,下面代码段中的版本可能已经过时,如有需要,请使...【详细内容】
2022-05-07  Tags: .NET  点击:(76)  评论:(0)  加入收藏
之前,我们已经了解了ASP.NET Core中的身份认证,现在,我们来聊一下授权。老规矩,示例程序源码XXTk.Auth.Samples已经提交了,需要的请自取。概述ASP.NET Core中的授权方式有很多,我...【详细内容】
2022-04-20  Tags: .NET  点击:(143)  评论:(0)  加入收藏
序言本文将分别介绍 Authentication(认证) 和 Authorization(授权)。并以简单的例子在 ASP.NET Core 6.0 的 WebAPI 中分别实现这两个功能。 相关名词Authentication 和 Author...【详细内容】
2022-04-18  Tags: .NET  点击:(197)  评论:(0)  加入收藏
前言由于客户网络安全限制,连接到互联网的设备不能访问内网。需要先从客户端应用中导出数据到文件,再将文件复制到U盘,最后通过内网机器上传数据。如何保证,在复制、传输过程中,...【详细内容】
2022-03-22  Tags: .NET  点击:(125)  评论:(0)  加入收藏
前言几乎所有.NET序列化程序的实现基础都是反射。下列代码是Newtonsoft.Json的实现:protectedvirtualJsonPropertyCreateProperty(MemberInfomember,MemberSerializationmemb...【详细内容】
2021-12-28  Tags: .NET  点击:(207)  评论:(0)  加入收藏
中间件是一种装配到应用管道以处理请求和响应的软件。 ASP.NET Core 提供了一组丰富的内置中间件组件,但在某些情况下,你可能需要写入自定义中间件。备注本主题介绍如何编写基...【详细内容】
2021-12-17  Tags: .NET  点击:(128)  评论:(0)  加入收藏
Intro.NET 6 中引入了一个新的 Timer —— System.Threading.PeriodicTimer,和之前的几个 Timer 相比一个最大的区别就是,新的 PeriodicTimer 的事件处理可以比较方...【详细内容】
2021-12-06  Tags: .NET  点击:(176)  评论:(0)  加入收藏
▌简易百科推荐
编译和反编译.NET 中的编译是把开发人员写的 C# 代码转化为计算机可理解的代码的过程,也就是中间语言代码(IL代码)。在这个过程中,C# 源代码被转换为可执行文件(exe或者dll 文件)...【详细内容】
2022-07-15  IT狂人日记    Tags:.NET   点击:(1)  评论:(0)  加入收藏
我们在开发 webapi 项目时如果遇到 api 接口需要同时支持多个版本的时候,比如接口修改了入参之后但是又希望支持老版本的前端(这里的前端可能是网页,可能是app,小程序 等等)进行...【详细内容】
2022-07-14  IT技术资源爱好者    Tags:.Net   点击:(3)  评论:(0)  加入收藏
什么是.NET.NET 是由 Microsoft 创建的开源开发平台,用于生成多种不同类型的应用程序,主要支持C#、F#及VB。.NET程序运行原理.NET程序的运行是由其虚拟机CLR(公共语言运行时)把...【详细内容】
2022-06-21  威步上海    Tags:.NET   点击:(26)  评论:(0)  加入收藏
Asp.Net Core Identity 是.Net自带的身份认证系统,支持用户界面 (UI) 登录功能,并且管理用户、密码、配置文件数据、角色、声明、令牌、电子邮件确认等等。使用Visual Studio...【详细内容】
2022-06-05  海椰人  博客园  Tags:.Net   点击:(35)  评论:(0)  加入收藏
安装Hangfire新建ASP.NET Core空 项目,.Net Core版本3.1 往*.csproj添加包引用,添加新的PackageReference标记。如下所示。请注意,下面代码段中的版本可能已经过时,如有需要,请使...【详细内容】
2022-05-07  壮志林云    Tags:.NET   点击:(76)  评论:(0)  加入收藏
 B/S架构的Web程序几乎占据了应用软件的绝大多数市场,但是C/S架构的WinForm、WPF客户端程序依然具有很实用的价值,如设计类软件 AutoCAD与Autodesk Revit、WPS、IT类的集成开...【详细内容】
2022-04-27  IT技术资源爱好者  博客园  Tags:.NET   点击:(153)  评论:(0)  加入收藏
前几天有个老项目找到我,有多老呢?比我工作年限都长,见到这个项目我还得叫一声前辈。这个项目目前使用非常稳定,十多年了没怎么更新过,现在客户想加一个小功能:在线预览Word文档。...【详细内容】
2022-04-27  海椰人  博客园  Tags:.Net   点击:(65)  评论:(0)  加入收藏
之前,我们已经了解了ASP.NET Core中的身份认证,现在,我们来聊一下授权。老规矩,示例程序源码XXTk.Auth.Samples已经提交了,需要的请自取。概述ASP.NET Core中的授权方式有很多,我...【详细内容】
2022-04-20  日行四善  博客园  Tags:授权   点击:(143)  评论:(0)  加入收藏
序言本文将分别介绍 Authentication(认证) 和 Authorization(授权)。并以简单的例子在 ASP.NET Core 6.0 的 WebAPI 中分别实现这两个功能。 相关名词Authentication 和 Author...【详细内容】
2022-04-18  芦荟柚子茶  博客园  Tags:ASP.NET   点击:(197)  评论:(0)  加入收藏
前言由于客户网络安全限制,连接到互联网的设备不能访问内网。需要先从客户端应用中导出数据到文件,再将文件复制到U盘,最后通过内网机器上传数据。如何保证,在复制、传输过程中,...【详细内容】
2022-03-22  My IO    Tags:.NET Core   点击:(125)  评论:(0)  加入收藏
站内最新
站内热门
站内头条