之前开发中遇见了一个让人很头疼的问题,java.lang.OutOfMemoryError 栈内存溢出。主要发生情况为,在本机开启服务器测试完全没有发生任何错误,但是当部署到客户服务器上时,就会发生这个错误,同样的代码,同样的数据库结构,以及同样的数据,在两个不同环境下运行会发生不同的结果,所以暂时断定与代码无关。
首先我去网上查了一下这个错误,网上说以下几种情况会引起内存溢出:
- 内存中加载的数据量过于庞大,如一次从数据库取出过多数据;
- 集合类中有对对象的引用,使用完后未清空,使得JVM不能回收;
- 代码中存在死循环或循环产生过多重复的对象实体;
- 使用的第三方软件中的BUG;
- 启动参数内存值设定的过小;
于是我将后端所有一次操作大量数据的查询接口全部分页处理,例如:一次性查询1000条的数据,改为一次查询100条,查询10次。经过一系列的修改后,再次使用服务器来运行,问题竟然解决了,不再出现内存溢出情况。
我以为事情到这里就结束了,万万没想到,大概半个月之后,这个错误竟然又一次出现了。那么,基于上边所说的五种情况,我们已经排除掉了三种。看到第二条,即:集合类中有对对象的引用,使用完后未清空,使得JVM不能回收;因为上次修改分页之后当时是没有再出现问题的,问题出现的时候是在半个月之后,所以我想,可能是后端代码中有的地方对象使用过后没有清空,从而多次长时间调用,会一点一点占用JVM的内存,时间一长,将JVM内存占用饱和,所以才会再半个月后又再次发生错误?
然后我排查了一遍所有后端代码,但是并没有发现不会被回收的情况。因为我们项目使用的是标准的SSM框架,所以后端代码的结构一目了然,并没有全局变量没有被回收的情况,所有的全局变量都是通过spring MVC 注解形式注入。JVM虚拟机的回收机制中,在结束变量所在作用域后,就会被JVM自主回收,所以我将第二种情况也排除掉了。
那么事情就变得简单了,只剩下最后一种情况了:JVM分配的内存空间太小而导致内存溢出。
由此在度娘下,找到了tomcat的一种查看内存状态的方法;
打开tomcat文件目录下的这个位置:E:\apache-tomcat-7.0.52\conf在conf文件夹下编辑tomcat-users.xml文件,将</tomcat-users>之前 <!.. ..> that surrounds them.-->之后的代码注释解开,并改为
<role rolename="tomcat"/>
<role rolename="role1"/>
<role rolename="manage-gui"/>
<role rolename="admin-gui"/>
<user username="tomcat" password="qwe123" roles="manager-gui"/>
<user username="both" password="qwe123" roles="tomcat,role1"/>
<user username="admin" password="qwe123" roles="manager-gui"/>
password项自己设置,此为多个权限不同的账号,我们用到的是最后一个。
然后启动服务器,在浏览器中输入http://服务器的IP地址:端口号/
例如:http://192.168.2.111:8080/
打开后页面:
点击server status 就会看到JVM的内存情况,但是这是非实时动态显示的,需要不停的刷新来观察:
左上角显示JVM的最大内存,最小内存,与空闲内存,当空闲内存不足时,便会内存溢出,而我们避免这种情况发生的话就需要修改最大值和最小值来扩大JVM的内存。
那么好吧,要怎么修改呢?
打开tomcat中的bin文件夹:路径为:E:\apache-tomcat-7.0.52\bin。点击编辑bin下的catalina.bat文件:
在最上方位置加入你要为JVM指定内存空间大小的配置:
四个参数分别代表:
-Xms
JVM初始分配的堆内存
-Xmx JVM最大允许分配的堆内存,按需分配
-XX:PermSize JVM初始分配的非堆内存
-XX:MaxPermSize JVM最大允许分配的非堆内存,按需分配
按照需求来分配就可以了,但是又发现一种情况让我非常无奈,tomcat7.0 有两种版本:一种是如上所述,使用startup.bat来开启,用shutdown.bat来关闭的,大多数的都是这个版本,包括我自己的电脑也是。但是!在客户的服务器上tomcat的bin文件夹下是这样的:
这就非常尴尬了,没有catalina.bat文件,要怎么配置JVM内存?
好吧,经过两个多小时的百度,终于在一篇不太起眼的大神文章中找到了解决办法:利用注册表来修改JVM内存配置,来看看具体步骤吧:
首先用命令方式打开注册表:
win7 32位系统如下:
HKEY_LOCAL_MACHINE\SOFTWARE\Apache Software Foundation\Procrun 2.0\tomcat8\Parameters\Java
Win7 X64系统则位于
HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Apache Software Foundation\Procrun 2.0\tomcat8\Parameters\Java
右键jvmMs编辑JVM初始分配的堆内存:
同样的方法设置jvmMx最大允许分配的堆内存,分配PermSize和MaxPermSize右键编辑options:
这样重启tomcat后在上述页面中查看JVM内存状态,就会发现内存已经变更成功了!