最近开发的时候发现了一个list.add()方法导致内存溢出的问题,花了一上午才搞定,想想还是有必要写下来分享一下。
首先说一下导致内存溢出的原因:
1、内存中加载的数据量过于庞大,如一次从数据库取出过多数据;
2、集合类中有对对象的引用,使用完后未清空,使得JVM不能回收;
3、代码中存在死循环或循环产生过多重复的对象实体;
4、使用的第三方软件中的BUG;
5、启动参数内存值设定的过小;
此错误常见的错误提示:
1、tomcat:java.lang.OutOfMemoryError: PermGen space
2、tomcat:java.lang.OutOfMemoryError: Java heap space
3、weblogic:Root cause of ServletException java.lang.OutOfMemoryError
4、resin:java.lang.OutOfMemoryError
5、java:java.lang.OutOfMemoryError
下面主要说一下前两种溢出的解决办法:
第一种 OutOfMemoryError: PermGen space
发生这种问题的原意是程序中使用了大量的jar或class,使java虚拟机装载类的空间不够,与Permanent Generation space有关。解决这类问题有以下两种办法:
1、 增加java虚拟机中的XX:PermSize和XX:MaxPermSize参数的大小,其中XX:PermSize是初始永久保存区域大小,XX:MaxPermSize是最大永久保存区域大小。在tomcat/bin/catalina.bat脚本的echo Using CATALINA_BASE: “%CATALINA_BASE%”前增加一行:
set JAVA_OPTS=%JAVA_OPTS% -server -Xms512m -Xmx512m -XX:MaxNewSize=512m
如果是windows服务器还可以在系统环境变量中设置。感觉用tomcat发布sprint+struts+hibernate架构的程序时很容易发生这种内存溢出错误。使用上述方法,我成功解决了部署ssh项目的tomcat服务器经常宕机的问题。
2、清理应用程序中web-inf/lib下的jar,如果tomcat部署了多个应用,很多应用都使用了相同的jar,可以将共同的jar移到tomcat共同的lib下,减少类的重复加载。这种方法是网上部分人推荐的,我没试过,但感觉减少不了太大的空间,最靠谱的还是第一种方法。
第二种 OutOfMemoryError: Java heap space
发生这种问题的原因是java虚拟机创建的对象太多,在进行垃圾回收之间,虚拟机分配的到堆内存空间已经用满了,与Heap space有关。JVM在启动的时候会自动设置Heap size的值,其初始空间(即-Xms)是物理内存的1/64,最大空间(-Xmx)是物理内存的1/4。解决这类问题有两种思路:
1、 检查程序,看是否有死循环或不必要地重复创建大量对象,是否有资源没有及时释放。找到原因后,修改程序和算法。
2、在确定程序没有bug的情况下,可以利用JVM提供的-Xmn -Xms -Xmx等选项可进行设置,增加Java虚拟机中Xms(初始堆大小)和Xmx(最大堆大小)参数的大小。在tomcat/bin/catalina.bat脚本的echo Using CATALINA_BASE: “%CATALINA_BASE%”前加入set JAVA_OPTS=%JAVA_OPTS% -server -Xms512m -Xmx512m -XX:MaxNewSize=512m ,并扩大JVM虚拟机的启动内存eclipse->Window->preferences->java->Installed JREs->edit,加入-Xms512m -Xmx512m,如图:
但是这个方法也只是治标不治本,加载更多的资源时还是会发生内存溢出。根本的办法还是优化算法,及时释放占用内存资源大的进程。例如采用list存储从数据库读取的大量数据时,是不是可以采用分次读取,或者采用outputstream先写到本地文件等等。
顺便提一嘴,带buffer缓存的流能实现快速读写,例如:DataOutputStream os = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(filePath)));
更多的内存优化算法还需大家自己钻研!!!