互联网服务中间件

开发中的内存溢出问题,是架构设计问题?还是编码问题?

内存溢出(Out Of Memory,简称oom) 是指应用系统中无法回收内存或使用的内存过多,导致程序运行所需内存大于虚拟机提供的最大内存。内存溢出是比较常见的一种应用程序性能问题,一旦发生,则系统的可用内存和性能持续下降;最终将导致内存不足(OutOfMemory),系统彻底宕掉,不能响应任何...显示全部
内存溢出(Out Of Memory,简称oom) 是指应用系统中无法回收内存或使用的内存过多,导致程序运行所需内存大于虚拟机提供的最大内存。

内存溢出是比较常见的一种应用程序性能问题,一旦发生,则系统的可用内存和性能持续下降;最终将导致内存不足(OutOfMemory),系统彻底宕掉,不能响应任何请求,其危害相当严重。

内存溢出可能产生的原因:
1、一次性加载到内存的数据量过于庞大,使得JAVA堆空间耗尽;
2、配置参数指定的JVM堆最大值太小;
3、内存泄露(Memory leak)问题。

我们在日常的技术开发工作中,偶然会遇到内存泄露/溢出的问题。虽然问题并不常见,但偶然的发生也使得技术应用开发者非常头疼。企业应用程序中的内存泄漏会导致大量的危急情况。付出的代价包括用于进行分析的时间和资金、生产环境中开销巨大的停机时间、压力以及对应用程序和框架丧失信心。

那么开发中常常导致的内存溢出(oom)是架构设计的问题呢?还是开发编码的问题呢?


辩论时间:2010年9月1日-2010年9月8日10:00

活动规则:

1、在为期一周的辩论时间里,大家可以投票给你支持的一方,并可对支持方发表自己的观点(发布观点时,请选择支持方);

2、在活动结束后,根据正反方投票数决定辩论赛的胜出方,胜出方的支持者将再获得20W币奖励;

2、在辩论结束后,我们将把大家发表的观点整理出来(像顶!支持!等垃圾回复贴排除!),开设1个投票贴,最后由会员集中投票,选出本次辩论的最佳观点。最佳观点的发表者将获得IBM保温杯一个+《WebSphere中国》往期杂志1本

baowenbei.jpg



收起
参与44

查看其它 43 个回答mfkqwyc86的回答

mfkqwyc86mfkqwyc86技术经理风哥
三、 JVM如何设置虚拟内存
提示:在 JVM中如果 98%的时间是用于 GC且可用的 Heap size 不足 2%的时候将抛出此异常信息。

提示: Heap Size 最大不要超过可用物理内存的 80%,一般的要将 -Xms和 -Xmx选项设置为相同,而 -Xmn为 1/4的 -Xmx值。

提示: JVM初始分配的内存由 -Xms指定,默认是物理内存的 1/64; JVM最大分配的内存由 -Xmx指定,默认是物理内存的 1/4。

默认空余堆内存小于 40%时, JVM就会增大堆直到 -Xmx的最大限制;空余堆内存大于 70%时, JVM会减少堆直到 -Xms的最小限制。因此服务器一般设置 -Xms、 -Xmx相等以避免在每次 GC 后调整堆的大小。

提示:假设物理内存无限大的话, JVM内存的最大值跟操作系统有很大的关系。

简单的说就 32位处理器虽然可控内存空间有 4GB,但是具体的操作系统会给一个限制,

这个限制一般是 2GB-3GB(一般来说 Windows系统下为 1.5G-2G, Linux系统下为 2G-3G), 而 64bit以上的处理器就不会有限制了

提示:注意:如果 Xms超过了 Xmx值,或者堆最大值和非堆最大值的总和超过了物理内 存或者操作系统的最大限制都会引起服务器启动不起来。

提示:设置 NewSize、 MaxNewSize相等, “new”的大小最好不要大于 “old”的一半,原因是 old区如果不够大会频繁的触发 “主 ” GC ,大大降低了性能

JVM使用 -XX:PermSize设置非堆内存初始值,默认是物理内存的 1/64;

由 XX:MaxPermSize设置最大非堆内存的大小,默认是物理内存的 1/4。

解决方法:手动设置 Heap size

修改 TOMCAT_HOME/bin/catalina.bat

在“ echo “Using CATALINA_BASE: $CATALINA_BASE””上面加入以下行:

JAVA_OPTS=”-server -Xms800m -Xmx800m -XX:MaxNewSize=256m”   

四、性能检查工具使用
定位内存泄漏:

JProfiler 工具主要用于检查和跟踪系统(限于 Java 开发的)的性能。 JProfiler 可以通过时时的监控系统的内存使用情况,随时监视垃圾回收,线程运行状况等手段,从而很好的监视 JVM 运行情况及其性能。


1. 应用服务器内存长期不合理占用,内存经常处于高位占用,很难回收到低位;

2. 应用服务器极为不稳定,几乎每两天重新启动一次,有时甚至每天重新启动一次;

3. 应用服务器经常做 Full GC(Garbage Collection),而且时间很长,大约需要 30-40秒,应用服务器在做 Full GC的时候是不响应客户的交易请求的,非常影响系统性能。

因为开发环境和产品环境会有不同,导致该问题发生有时会在产品环境中发生, 通常可以使用工具跟踪系统的内存使用情况,在有些个别情况下或许某个时刻确实 是使用了大量内存导致 out of memory,这时应继续跟踪看接下来是否会有下降,

如果一直居高不下这肯定就因为程序的原因导致内存泄漏。

五、不健壮代码的特征及解决办法
1 、尽早释放无用对象的引用。好的办法是使用临时变量的时候,让引用变量在退出活动域后,自动设置为 null ,暗示垃圾收集器来收集该对象,防止发生内存泄露。

对于仍然有指针指向的实例, jvm 就不会回收该资源 , 因为垃圾回收会将值为 null 的对象作为垃圾,提高 GC 回收机制效率;

2 、我们的程序里不可避免大量使用字符串处理,避免使用 String ,应大量使用 StringBuffer ,每一个 String 对象都得独立占用内存一块区域;

String str = “aaa”;     
   
String str2 = “bbb”;     
   
String str3 = str + str2;// 假如执行此次之后 str ,str2 以后再不被调用 , 那它就会被放在内存中等待 Java 的 gc 去回收 , 程序内过多的出现这样的情况就会报上面的那个错误 , 建议在使用字符串时能使用 StringBuffer 就不要用 String, 这样可以省不少开销;     

3 、尽量少用静态变量,因为静态变量是全局的, GC 不会回收的;

4 、避免集中创建对象尤其是大对象, JVM 会突然需要大量内存,这时必然会触发 GC 优化系统内存环境;显示的声明数组空间,而且申请数量还极大。

这是一个案例想定供大家警戒:

使用jspsmartUpload作文件上传,现在运行过程中经常出现java.outofMemoryError的错误,用top命令看看进程使用情况,发现内存不足2M,花了很长时间,发现是jspsmartupload的问题。把jspsmartupload组件的源码文件(class文件)反编译成Java文件,如梦方醒:

m_totalBytes = m_request.getContentLength();         
m_binArray = new byte[m_totalBytes];      

变量m_totalBytes表示用户上传的文件的总长度,这是一个很大的数。如果用这样大的数去声明一个byte数组,并给数组的每个元素分配内存空间,而且m_binArray数组不能马上被释放,JVM的垃圾回收确实有问题,导致的结果就是内存溢出。

jspsmartUpload为什末要这样作,有他的原因,根据RFC1867的http上传标准,得到一个文件流,并不知道文件流的长度。设计者如果想文件的长度,只有操作servletinputstream一次才知道,因为任何流都不知道大小。只有知道文件长度了,才可以限制用户上传文件的长度。为了省去这个麻烦,jspsmartUpload设计者直接在内存中打开文件,判断长度是否符合标准,符合就写到服务器的硬盘。这样产生内存溢出,这只是我的一个猜测而已。

所以编程的时候,不要在内存中申请大的空间,因为web服务器的内存有限,并且尽可能的使用流操作,例如

byte[] mFileBody = new byte[512];   
        Blob vField= rs.getBlob("FileBody");     
     InputStream instream=vField.getBinaryStream();   
     FileOutputStream fos=new FileOutputStream(saveFilePath+CFILENAME);   
         int b;   
                      while( (b =instream.read(mFileBody)) != -1){   
                       fos.write(mFileBody,0,b);   
                        }   
       fos.close();   
     instream.close();   

5 、尽量运用对象池技术以提高系统性能;生命周期长的对象拥有生命周期短的对象时容易引发内存泄漏,例如大集合对象拥有大数据量的业务对象的时候,可以考虑分块进行处理,然后解决一块释放一块的策略。

6 、不要在经常调用的方法中创建对象,尤其是忌讳在循环中创建对象。可以适当的使用 hashtable , vector 创建一组对象容器,然后从容器中去取那些对象,而不用每次 new 之后又丢弃

7 、一般都是发生在开启大型文件或跟数据库一次拿了太多的数据,造成 Out Of Memory Error 的状况,这时就大概要计算一下数据量的最大值是多少,并且设定所需最小及最大的内存空间值。
系统集成 · 2010-09-07
浏览1178

回答者

mfkqwyc86
技术经理风哥
擅长领域: 中间件服务器存储

mfkqwyc86 最近回答过的问题

回答状态

  • 发布时间:2010-09-07
  • 关注会员:0 人
  • 回答浏览:1178
  • X社区推广