有效预防.NET应用程序OOM的经验备忘

简介:

根据个人的开发和系统调优经验,大部分的内存溢出(及内存泄漏)都和不好的开发习惯有直接关系,有几个开发经验可以有效预防OOM,总结下贴出来和大家分享。

 

一、批量和分页

老生常谈的话题,简单,但是非常实用。

每个合格的coder对数据的处理,必须要有分页或批量多次的意识。大数据量的读取或查询结果集是内存占用大户,是系统性能下降的直接原因之一。

在典型的互联网web应用中,数据量较大且高并发的情况下,不分页,或者不进行批量处理,每次总是取出很多用户数据,很容易造成内存开销过大,系统内存吃紧。再比如我们有时候进行文件操作,读取文件内容的时候就要斟酌考虑文件有多大。

如果你的项目中还在出现不分青红皂白一次查询返回N(N有多大?)条记录的DataSet、DataTable或者列表记录等等情况,或者查询大量数据写入临时表,或者一次读取很大文件内容......呵呵。

 

二、慎用静态

这个也是常见但是比较隐式的引起内存泄露的元凶之一。

静态类、静态字段、静态属性,静态委托,静态方法…静态的好处当然显而易见,比如调用方便,常驻内存提高性能等等,所以,有些代码索性静态到底,除了实体层,在表现层(说起来非常可怕,我曾经在web应用程序中看到竟然有人名目张胆地大肆使用静态字段),数据访问层、业务逻辑层、公共组件、配置管理等等等等,能静态的全给它用上静态。

比起大数据查询造成的常发性的内存不足,使用静态太多的应用程序一时半会也不会内存泄露。可随着系统的运行,静态的东西越来越多,内存开销也就越大,由于GC的回收策略,无效的静态所占内存又不容易及时释放,久而久之就造成了内存不足。

使用静态的情况在分层应用程序中非常常见,而且由于它的好处容易得到体现而隐藏的风险不容易暴露出来,所以很多程序员还是非常执着地使用静态。

 

三、二方库、三方库,非托管资源,优先使用Dispose模式

有些应用程序需要借助包装的二方库或者三方库,或者使用了非托管资源,如com组件等等,由于.NET自动内存管理和回收,很多人觉得我用一下完成功能就Over了。

实际情况是你调用了别人的库,别人的库也很可能当仁不让地占用了你的内存而你还不自知。

每次调用别人的资源都应该有个警觉性:用你的类库可以,占用我的(内存)不行。

如果你熟悉自动内存管理,熟悉GC,理解Dispose模式,那么一定会在调用别人的资源的时候想着还是using一下为妙,或者,强制赋个null也是举手之劳,要相信某些良好的编程习惯可以让自动内存管理更有效。怕的就是很多人拿来主义,测试不充分,自己调用成功功能完成开发就OK了,交接给别人自己走人。

 

四、减少字符串临时对象

这个实在是太熟悉不过了,不论是什么形式的应用程序,哪里能少得了字符串的身影?

看到它们有人条件反射似地想到拼接字符串,想到驻留池等等等等。

没错,不合理地使用String进行操作也会造成内存不足异常,而且这绝不是耸人听闻。

举例来说,对于String的+=,很多应用程序中这个操作层出不穷。我们都知道+=操作会造成很多临时字符串对象,这些对象由于CLR对字符串的驻留处理,容易占用内存空间。如果是高并发的web应用程序,而字符串操作随处可见,且字符串的长度又不确定地长,前端页面各种各样的拼接,久而久之,内存占用就会是一个重大问题。CLR对字符串的优化处理使得字符串不被优先回收,如果字符串操作频繁,临时字符串较长(比如大于等于85000字节)而出现大对象堆的分配,那么更容易出现内存泄露。

很多人可能都会想到如何优化程序去降低string的临时对象的生成概率。对的,StringBuilder的出现就顺理成章了。

 

五、其他经验

1、Session的不当使用,尤其是使用InProc模式的会话,为了保持状态而选择使用Session,如用户访问量较大将极大消耗服务器资源,而且会出现Session丢失的不稳定现象,所以一般的站点都选择restful的无状态服务;

2、使用较为复杂的数据结构,比如字典里面嵌套字典,字典的键和值也使用字典,曾经碰到过一个非常奇葩的项目,至少5层字典嵌套…有人会反驳说字典是引用类型,而且自动垃圾回收等等等等等等,在OOM面前一切雄辩都苍白无力;

3、过深的继承链,这里尤其要说的是类继承,熟悉垃圾回收的应该都清楚GC回收原理,继承的存在有可能延长类的生命周期而不利于及时回收,所以,如果实际项目中出现继承的深度超过两位数,那一定是抽象出现问题了,重构是必然选择;

4、一些多媒体处理程序的开发中内存泄露情况也非常常见,比如使用GDI+开发画图程序等等,内存消耗严重,这时候托管代码开启dispose模式无比重要;

5、在使用lucene.net的过程中发现有时候创建索引会出现OOM,数据量上去以后,内存不足几乎不可避免,这个时候就必须考虑重新调整架构拆分索引文件分布处理了;

6、有时候调用office组件进行一些报表处理,发现内存好像一下子少了好多?使用7z压缩组件,如果多线程调用,好像也有内存吃紧的现象?

7、调用第三方邮件组件处理邮件和附件,CPU和内存开销都很不能让人满意;

8、不断地注册事件(Event)  请参考Artech的这篇这篇

……

更多其他容易导致OOM的开发经验等你来补充。

 

六、警惕大对象

本文前面分析的几种情况流于经验和表象,还有一种直达问题本质的内存泄露原因需要分析。

如果你深入理解了内存回收原理以及大对象和大对象堆(Large Object Heap,LOH),那么大对象导致的内存碎片化问题就很好理解了。

简单来说:

1、任何大于等于85000字节的对象都被自动视为大对象,大对象从特殊的大对象堆中分配。大对象堆和小对象堆一样进行终结和释放,但是GC回收算法从来不对大对象堆(Large Object Heap)进行内存压缩整理,因为在堆中下移85000字节或更大的内存块会浪费太多的CPU时间;

2、在.NET中,CLR采用基于代(generation)的垃圾回收,大对象总被认为是第2代(generation)的一部分,GC分析哪些对象不可达,优先分析第0代和第1代,第2代的对象通常被认为长时间存活。

正是由于1和2所述的两个原因(主要原因还是第1个),在垃圾回收过程中容易造成内存碎片。这里推荐一篇老外写的流传甚广的文章供参考:the dangers of the LOH

随着应用程序的运行,如果LOH导致的内存碎片越来越多,内存有效使用率下降会非常严重,比如我们在web应用程序中+=拼接字符串(见第4条的分析),如果大于等于85000字节的字符串临时对象很多,那么用户量一上去,随着系统的运行,GC回收压力越来越大OOM的风险会变得更高。

虽然内存碎片化导致的OOM看上去似乎无解,但是如果写程序的人仔细分析解决问题,想方设法主动降低创建大对象的频率,那么内存泄露的可能就会降低,足够优秀健壮的程序不能彻底解决OOM,但是我们完全可以将风险发生的情况将至最低可能。

一个足够合格的coder肯定需要具备充足的分析和解决OOM问题的准备和经验,有很多分析和检查OOM的工具如ANTS Memory Profiler,还可以通过调试利器如windbg对内存dump文件进行分析。用好这些工具,让OOM无所遁形也不失为解决之道。

最后,本文是今年春节前的最后一篇,提前祝大家节日快乐。文章写得太精彩了,完全可以用流水账来形容╮(╯_╰)╭,自我鼓励一下结束,哈哈。





本文转自JeffWong博客园博客,原文链接:http://www.cnblogs.com/jeffwongishandsome/archive/2013/02/04/2878919.html,如需转载请自行联系原作者

目录
相关文章
|
5月前
|
存储 Shell Linux
快速上手基于 BaGet 的脚本自动化构建 .net 应用打包
本文介绍了如何使用脚本自动化构建 `.net` 应用的 `nuget` 包并推送到指定服务仓库。首先概述了 `BaGet`——一个开源、轻量级且高性能的 `NuGet` 服务器,支持多种存储后端及配置选项。接着详细描述了 `BaGet` 的安装、配置及使用方法,并提供了 `PowerShell` 和 `Bash` 脚本实例,用于自动化推送 `.nupkg` 文件。最后总结了 `BaGet` 的优势及其在实际部署中的便捷性。
220 10
|
1月前
|
C# Android开发 iOS开发
2025年全面的.NET跨平台应用框架推荐
2025年全面的.NET跨平台应用框架推荐
85 23
|
3月前
|
开发框架 监控 .NET
【Azure App Service】部署在App Service上的.NET应用内存消耗不能超过2GB的情况分析
x64 dotnet runtime is not installed on the app service by default. Since we had the app service running in x64, it was proxying the request to a 32 bit dotnet process which was throwing an OutOfMemoryException with requests >100MB. It worked on the IaaS servers because we had the x64 runtime install
|
3月前
|
JSON 算法 安全
JWT Bearer 认证在 .NET Core 中的应用
【10月更文挑战第30天】JWT(JSON Web Token)是一种开放标准,用于在各方之间安全传输信息。它由头部、载荷和签名三部分组成,用于在用户和服务器之间传递声明。JWT Bearer 认证是一种基于令牌的认证方式,客户端在请求头中包含 JWT 令牌,服务器验证令牌的有效性后授权用户访问资源。在 .NET Core 中,通过安装 `Microsoft.AspNetCore.Authentication.JwtBearer` 包并配置认证服务,可以实现 JWT Bearer 认证。具体步骤包括安装 NuGet 包、配置认证服务、启用认证中间件、生成 JWT 令牌以及在控制器中使用认证信息
172 2
|
4月前
|
SQL XML 关系型数据库
入门指南:利用NHibernate简化.NET应用程序的数据访问
【10月更文挑战第13天】NHibernate是一个面向.NET的开源对象关系映射(ORM)工具,它提供了从数据库表到应用程序中的对象之间的映射。通过使用NHibernate,开发者可以专注于业务逻辑和领域模型的设计,而无需直接编写复杂的SQL语句来处理数据持久化问题。NHibernate支持多种数据库,并且具有高度的灵活性和可扩展性。
71 2
|
4月前
|
开发框架 .NET API
Windows Forms应用程序中集成一个ASP.NET API服务
Windows Forms应用程序中集成一个ASP.NET API服务
124 9
|
5月前
|
数据采集 JSON API
.NET 3.5 中 HttpWebRequest 的核心用法及应用
【9月更文挑战第7天】在.NET 3.5环境下,HttpWebRequest 类是处理HTTP请求的一个核心组件,它封装了HTTP协议的细节,使得开发者可以方便地发送HTTP请求并接收响应。本文将详细介绍HttpWebRequest的核心用法及其实战应用。
211 6
|
6月前
|
Linux iOS开发 开发者
跨平台开发不再难:.NET Core如何让你的应用在Windows、Linux、macOS上自如游走?
【8月更文挑战第28天】本文提供了一份详尽的.NET跨平台开发指南,涵盖.NET Core简介、环境配置、项目结构、代码编写、依赖管理、构建与测试、部署及容器化等多个方面,帮助开发者掌握关键技术与最佳实践,充分利用.NET Core实现高效、便捷的跨平台应用开发与部署。
635 3
|
6月前
|
开发框架 监控 安全
.NET 应用程序安全背后究竟隐藏着多少秘密?从编码到部署全揭秘!
【8月更文挑战第28天】在数字化时代,.NET 应用程序的安全至关重要。从编码阶段到部署,需全面防护以保障系统稳定与用户数据安全。开发者应遵循安全编码规范,实施输入验证、权限管理和加密敏感信息等措施,并利用安全测试发现潜在漏洞。此外,部署时还需选择安全的服务器环境,配置 HTTPS 并实时监控应用状态,确保全方位防护。
80 3
|
6月前
|
缓存 Java API
【揭秘】.NET高手不愿透露的秘密:如何让应用瞬间提速?
【8月更文挑战第28天】本文通过对比的方式,介绍了针对 .NET 应用性能瓶颈的优化方法。以一个存在响应延迟和并发处理不足的 Web API 项目为例,从性能分析入手,探讨了使用结构体减少内存分配、异步编程提高吞吐量、EF Core 惰性加载减少数据库访问以及垃圾回收机制优化等多个方面,帮助开发者全面提升 .NET 应用的性能和稳定性。通过具体示例,展示了如何在不同场景下选择最佳实践,以实现更高效的应用体验。
82 3

热门文章

最新文章