大部分的内存溢出(及内存泄漏)都和不好的开发习惯有直接关系,以下几个方式可以有效预防OOM。

一、批量和分页

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

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

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

二、慎用静态

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

  静态类、静态字段、静态属性,静态委托,静态方法…静态的好处当然显而易见,比如调用方便,常驻内存提高性能等等,所以,有些代码索性静态到底,除了实体层,在表现层,数据访问层、业务逻辑层、公共组件、配置管理等等,能静态的全给它用上静态。

  比起大数据查询造成的常发性的内存不足,使用静态太多的应用程序一时半会也不会内存泄露。可随着系统的运行,静态的东西越来越多,内存开销也就越大,由于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)

六、警惕大对象

  以下是一种直达问题本质的内存泄露原因需要分析。

  如果你深入理解了内存回收原理以及大对象和大对象堆(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个),在垃圾回收过程中容易造成内存碎片。

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

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

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

.NET 之 有效预防.NET应用程序OOM的更多相关文章

  1. Android高效加载大图、多图解决方案,有效避免程序OOM

    高效加载大图片 我们在编写Android程序的时候经常要用到许多图片,不同图片总是会有不同的形状.不同的大小,但在大多数情况下,这些图片都会大于我们程序所需要的大小.比如说系统图片库里展示的图片大都是 ...

  2. Android LruCache 压缩图片 有效避免程序OOM

    转载请注明出处:http://blog.csdn.net/guolin_blog/article/details/9316683 本篇文章主要内容来自于Android Doc,我翻译之后又做了些加工, ...

  3. 每个人都要学的图片压缩终极奥义,有效解决 Android 程序 OOM

    # 由来 在我们编写 Android 程序的时候,几乎永远逃避不了图片压缩的难题.除了应用图标之外,我们所要显示的图片基本上只有两个来源: 来自网络下载 本地相册中加载 不管是网上下载下来的也好,还是 ...

  4. Android高效加载大图、多图解决方案,有效避免程序OOM(转)

    本篇文章主要内容来自于Android Doc,我翻译之后又做了些加工,英文好的朋友也可以直接去读原文. http://developer.android.com/training/displaying ...

  5. 一次容器化springboot程序OOM问题探险

    背景 运维人员反馈一个容器化的java程序每跑一段时间就会出现OOM问题,重启后,间隔大概两天后复现. 问题调查 一查日志 由于是容器化部署的程序,登上主机后使用docker logs Contain ...

  6. Java应用程序OOM分析

    内存泄露:申请使用完的内存没有释放,导致虚拟机不能再次使用该内存,此时这段内存就泄露了,因为申请者不用了,而又不能被虚拟机分配给别人用. 内存溢出:申请的内存超出了JVM能提供的内存大小,此时称之为溢 ...

  7. Android高效加载大图,多图解决方案,有效避免程序OOM异常

    收藏自:http://blog.csdn.net/guolin_blog/article/details/9316683 谷歌官方文档:http://developer.android.com/tra ...

  8. 垃圾回收机制GC知识再总结兼谈如何用好GC

    一.为什么需要GC 应用程序对资源操作,通常简单分为以下几个步骤: 1.为对应的资源分配内存 2.初始化内存 3.使用资源 4.清理资源 5.释放内存 应用程序对资源(内存使用)管理的方式,常见的一般 ...

  9. GC机制总结

    一.为什么需要GC 应用程序对资源操作,通常简单分为以下几个步骤: 1.为对应的资源分配内存 2.初始化内存 3.使用资源 4.清理资源 5.释放内存 应用程序对资源(内存使用)管理的方式,常见的一般 ...

随机推荐

  1. 关于phonegap的白名单机制

    今天在项目中发现了一个问题,使用phonegap开发的APP默认情况下可以将外部网页加载进入手机APP当中,这是相当危险的,同时也会给人一种APP非native的感觉. 可能遇见的一种情况是有些WiF ...

  2. 【BZOJ 1998】 1998: [Hnoi2010]Fsk物品调度(双向链表+并查集+置换)

    1998: [Hnoi2010]Fsk物品调度 Description 现在找工作不容易,Lostmonkey费了好大劲才得到fsk公司基层流水线操作员的职位.流水线上有n个位置,从0到n-1依次编号 ...

  3. 【LA 3641】 Leonardo's Notebook (置换群)

    [题意] 给出26个大写字母组成 字符串B问是否存在一个置换A使得A^2 = B [分析] 置换前面已经说了,做了这题之后有了更深的了解. 再说说置换群.   首先是群. 置换群的元素是置换,运算时是 ...

  4. 【UOJ #279】【UTR #2】题目交流通道

    http://uoj.ac/problem/279 先判断答案为0的情况,\(d(i,i)\neq 0\),\(d(i,j)\neq d(j,i)\),\(d(i,j)>d(i,k)+d(k,j ...

  5. [BZOJ5251][九省联考2018]劈配(网络流)

    5251: [2018多省省队联测]劈配 Time Limit: 10 Sec  Memory Limit: 512 MBSubmit: 33  Solved: 22[Submit][Status][ ...

  6. [BZOJ4815][CQOI2017]小Q的表格(莫比乌斯反演)

    4815: [Cqoi2017]小Q的表格 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 832  Solved: 342[Submit][Statu ...

  7. 【找规律】【DFS】Gym - 101174H - Pascal's Hyper-Pyramids

    二维下,如果把杨辉三角按照题目里要求的那样摆放,容易发现,第i行第j列的数(从0开始标号)是C(i+j,i)*C(j,j). 高维下也有类似规律,比如三维下,最后一层的数其实是C(i+j+k,i)*C ...

  8. 【树链剖分】bzoj2243 [SDOI2011]染色

    树链剖分模板题.线段树维护每个段中的颜色数.左端点颜色.右端点颜色. pushup: col[rt]=col[rt<<1]+col[rt<<1|1]-(Rcol[rt<& ...

  9. Problem A: 深入浅出学算法022-汉诺塔问题II

    #include<stdio.h> void hanio(int n,char a,char b,char c) { ) printf("%c->%c\n",a, ...

  10. lnmp配置信息 4核8g优化

    MYSQL   my.conf# The following options will be passed to all MySQL clients[client]#password       = ...