源:http://blog.chinaunix.net/uid-16728139-id-3123809.html
更严重的是
内存溢出与数据库锁表在系统开发和单元测试阶段并不容易被
发现,当系统正式
上线一般时间后,操作的并发量上来了,数据也
积累了一些,系统就容易出现内存溢出或是锁表的现象,而此时系统又不能随意停机或
重启,为修正BUG带来很大的困难。
2.内存溢出的分析内存溢出是指应用系统中存在无法回收的内存或使用的内存过多,最终使得程序运行要用到的内存大于虚拟机能提供的最大内存。为了解决Java中内存溢出问题,我们首先必须了解Java是
如何管理内存的。Java的内存管理就是对象的分配和释放问题。在Java中,内存的分配是由程序完成的,而内存的释放是由垃圾收集器(Garbage Collection,GC)完成的,
程序员不需要通过调用GC函数来释放内存,因为不同的
JVM实现者可能使用不同的
算法管理GC,有的是内存使用到达一定程度时,GC才开始工作,也有定时执行的,有的是中断式执行GC。但GC只能回收无用并且不再被其它对象引用的那些对象所占用的空间。Java的内存垃圾回收机制是从程序的主要运行对象开始检查引用链,当遍历一遍后发现没有被引用的孤立对象就作为垃圾回收。
l 内存中加载的数据量过于庞大,如一次从数据库取出过多数据;
l 代码中存在死
循环或循环产生过多重复的对象实体;
l 启动参数内存值设定的过小;
第一步,就是修改JVM启动参数,直接增加内存。这一点看上去似乎很简单,但很容易被忽略。JVM默认可以使用的内存为64M,Tomcat默认可以使用的内存为128MB,对于稍复杂一点的系统就会不够用。在某项目中,就因为启动参数使用的默认值,经常报“OutOfMemory”
错误。因此,-Xms,-Xmx参数一定不要忘记加。
查看日志对于分析内存溢出是非常重要的,通过仔细查看日志,分析内存溢出前做过哪些操作,可以大致定位有问题的模块。
l 检查代码中是否有死循环或
递归调用。
l 检查对数据库查询中,是否有一次获得全部数据的查询。一般来说,如果一次取十万条记录到内存,就可能引起内存溢出。这个问题比较隐蔽,在上线前,数据库中数据较少,不容易出问题,上线后,数据库中数据多了,一次查询就有可能引起内存溢出。因此对于数据库查询尽量采用分页的方式查询。
第四步,使用内存查看工具动态查看内存使用情况。某个项目上线后,每次系统启动两天后,就会出现内存溢出的错误。这种情况一般是代码中出现了缓慢的内存泄漏,用上面三个步骤解决不了,这就需要使用内存查看工具了。
通过以上四个步骤的分析与处理,基本能处理内存溢出的问题。当然,在这些过程中也需要相当的经验与敏感度,需要在实际的开发与调试过程中不断积累。
1.数据量过于庞大;死循环 ;静态变量和
静态方法过多;递归;无法确定是否被引用的对象;
说白了就是程序运行要用到的内存大于虚拟机能提供的最大内存就发生内存溢出了。 内存溢出的问题要看业务和系统大小而定,对于某些系统可能内存溢出不常见,但某些系统还是很常见的解决的方法,
一、内存溢出类型 1 、 java.lang.OutOfMemoryError: PermGen space
2 、 java.lang.OutOfMemoryError: Java heap space
注意:如果 Xms 超过了 Xmx 值,或者堆最大值和非堆最大值的总和超过了物理内存或者操作系统的最大
限制都会引起服务器启动不起来。
JVM 调用 GC 的频度还是很高的,主要两种情况下进行垃圾回收:
根据 GC 的机制,程序的运行会引起系统运行环境的变化,增加 GC 的触发机会。
一个是并不能解决内存资源耗空的局面,另外也会增加 GC 的消耗。
java把内存分两种:一种是栈内存,另一种是堆内存
2。堆内存用来存放由 new创建的对象和数组
堆的优势是可以动态分配内存大小,生存期也不必事先告诉编译器,因为它是在运行时动态分配内存的。缺点就是要在运行时动态分配内存,存取速度较慢;
java 堆分为三个区: New 、 Old 和 Permanent
新创建的对象被分配到 New 区,当该区被填满时会被 GC 辅助
线程移到 Old 区,当 Old 区也填满了会触发 GC 主线程遍历堆内存里的所有对象。 Old 区的大小等于 Xmx 减去 -Xmn
栈调整:参数有 +Use
DefaultStackSize -Xss256K,表示每个线程可申请 256k的栈空间
三、 JVM如何设置虚拟内存 提示:在 JVM中如果 98%的时间是用于 GC且可用的 Heap size 不足 2%的时候将抛出此
异常信息。
提示: JVM初始分配的内存由 -Xms指定,默认是物理内存的 1/64; JVM最大分配的内存由 -Xmx指定,默认是物理内存的 1/4。
提示:假设物理内存无限大的话, JVM内存的最大值跟操作系统有很大的关系。
这个限制一般是 2GB-3GB(一般来说 Windows系统下为 1.5G-2G, Linux系统下为 2G-3G), 而 64bit以上的
处理器就不会有限制了
提示:设置 NewSize、 MaxNewSize相等, “new”的大小最好不要大于 “old”的一半,原因是 old区如果不够大会频繁的触发 “主 ” GC ,大大降低了性能
由 XX:MaxPermSize设置最大非堆内存的大小,默认是物理内存的 1/4。
修改 TOMCAT_HOME/bin/catalina.bat
JAVA_OPTS=”-server -Xms800m -Xmx800m -XX:MaxNewSize=256m”
四、性能检查工具使用 定位内存泄漏:
1.
应用服务器内存长期不
合理占用,内存经常处于高位占用,很难回收到低位;
3. 应用服务器经常做 Full GC(Garbage Collection),而且时间很长,大约需要 30-40秒,应用服务器在做 Full GC的时候是不响应客户的交易请求的,非常影响系统性能。
如果一直居高不下这肯定就因为程序的原因导致内存泄漏。
对于仍然有指针指向的实例, jvm 就不会回收该资源 , 因为垃圾回收会将值为 null 的对象作为垃圾,提高 GC 回收机制效率;
String str = “aaa”;
String str2 = “bbb”;
String str3 = str + str2;// 假如执行此次之后 str ,str2 以后再不被调用 , 那它就会被放在内存中等待 Java 的 gc 去回收 , 程序内过多的出现这样的情况就会报上面的那个错误 , 建议在使用字符串时能使用 StringBuffer 就不要用 String, 这样可以省不少开销;
3 、尽量少用静态变量,因为静态变量是
全局的, GC 不会回收的;
这是一个案例想定供大家警戒:
m_totalBytes = m_request.getContentLength();
m_binArray = new byte[m_totalBytes];
变量m_totalBytes表示用户上传的文件的总长度,这是一个很大的数。如果用这样大的数去声明一个byte数组,并给数组的每个元素分配内存空间,而且m_binArray数组不能马上被释放,JVM的垃圾回收确实有问题,导致的结果就是内存溢出。
所以编程的时候,不要在内存中申请大的空间,因为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();
6 、不要在经常调用的方法中
创建对象,尤其是忌讳在循环中创建对象。可以适当的使用
hashtable , vector 创建一组对象容器,然后从容器中去取那些对象,而不用每次 new 之后又丢弃
7 、一般都是发生在
开启大型文件或跟数据库一次拿了太多的数据,造成 Out Of Memory Error 的状况,这时就大概要计算一下数据量的最大值是多少,并且设定所需最小及最大的内存空间值。