大部分的内存溢出(及内存泄漏)都和不好的开发习惯有直接关系,以下几个方式可以有效预防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. Aras Innovator 11 sp2 firefox客户端设置

    在上一篇文章<Aras Innovator 11 sp2 安装>后,服务器算是安装好了,还需要在使用的客户端进行设置才可以正常使用Aras Innovator 该篇为firefox设置,还 ...

  2. hdu 1874 畅通工程(spfa 邻接矩阵 邻接表)

    题目链接 畅通工程,可以用dijkstra算法实现. 听说spfa很好用,来水一发 邻接矩阵实现: #include <stdio.h> #include <algorithm> ...

  3. LOJ #6279. 数列分块入门 3-分块(区间加法、查询区间内小于某个值x的前驱(比其小的最大元素))

    #6279. 数列分块入门 3 内存限制:256 MiB时间限制:1500 ms标准输入输出 题目类型:传统评测方式:文本比较 上传者: hzwer 提交提交记录统计测试数据讨论 3   题目描述 给 ...

  4. 洛谷——P2681 众数

    P2681 众数 题目背景 Alice和Bob玩游戏 题目描述 Alice现在有一个序列a1.a2...an 现在她需要Bob支持询问一个区间内的众数,还要支持修改一个位置的ai 输入输出格式 输入格 ...

  5. HDU 6119 2017百度之星初赛B 小小粉丝度度熊 (二分)

    小小粉丝度度熊 Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Sub ...

  6. 【SpringMVC】一次处理项目中文乱码的经历

    一次处理项目中文乱码的经历 背景 今天把旧服务器上的项目转移到新服务器上,结果返回的json中的中文乱码了,觉得很奇怪,因为新服务器和旧服务器都是TX云,也不会有太大区别呀,于是乎开始了为期半天的蛋疼 ...

  7. OpenCV 学习之路(2) -- 操作像素

    本节内容: 访问像素值 用指针扫描图像 用迭代器扫描图像 编写高效的图像扫描循环 扫描图像并访问相邻像素 实现简单的图像运算 图像重映射 访问像素值 准备工作: 创建一个简单函数,用它在图像中加入椒盐 ...

  8. 求高精度幂(poj1001)

    Description Problems involving the computation of exact values of very large magnitude and precision ...

  9. START法则

    用途:在做项目总结以及阶段性报告等的时候,可以很好的帮自己对整个工作过程进行梳理和总结,很好的表现出自己分析问题的清晰性.条理性和逻辑性. 定义:STAR法则是情境(situation).任务(tas ...

  10. [BZOJ 1500] 维护序列

    Link: BZOJ 1500 传送门 Solution: 可能平衡树维护序列的所有操作都在这了吧…… 对序列的维护$fhq treap$和$Splay$都能做 有几个注意点: 1.维护序列时始终记得 ...