Tomcat的JVM配置:解决Out of memory:java head space

时间:2024-04-03 12:29:40

    之前开发中遇见了一个让人很头疼的问题,java.lang.OutOfMemoryError 栈内存溢出。主要发生情况为,在本机开启服务器测试完全没有发生任何错误,但是当部署到客户服务器上时,就会发生这个错误,同样的代码,同样的数据库结构,以及同样的数据,在两个不同环境下运行会发生不同的结果,所以暂时断定与代码无关。

    首先我去网上查了一下这个错误,网上说以下几种情况会引起内存溢出:

  1. 内存中加载的数据量过于庞大,如一次从数据库取出过多数据;
  2. 集合类中有对对象的引用,使用完后未清空,使得JVM不能回收;
  3. 代码中存在死循环或循环产生过多重复的对象实体;
  4. 使用的第三方软件中的BUG;
  5. 启动参数内存值设定的过小;
    因为之前已经排除代码的错误,以及后台没有使用第三方软件,所以第三种和第四种情况可以无视掉。那么,我便开始从第一种情况排查。首先,我怀疑服务器上的数据库可能与我自己电脑中的数据库版本不同或者配置有差异,例如:本机数据库与服务器上的数据库支持一次性操作的数据量不同,可能是本机的数据操作量比较大,所以在本机上没有错误,而服务器的数据库可一次性操作数据量小,所以内存溢出?

    于是我将后端所有一次操作大量数据的查询接口全部分页处理,例如:一次性查询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/

打开后页面:

Tomcat的JVM配置:解决Out of memory:java head space

Tomcat的JVM配置:解决Out of memory:java head space

点击server status 就会看到JVM的内存情况,但是这是非实时动态显示的,需要不停的刷新来观察:

Tomcat的JVM配置:解决Out of memory:java head space

Tomcat的JVM配置:解决Out of memory:java head space

左上角显示JVM的最大内存,最小内存,与空闲内存,当空闲内存不足时,便会内存溢出,而我们避免这种情况发生的话就需要修改最大值和最小值来扩大JVM的内存。

    

    那么好吧,要怎么修改呢?

    打开tomcat中的bin文件夹:路径为:E:\apache-tomcat-7.0.52\bin。点击编辑bin下的catalina.bat文件:

Tomcat的JVM配置:解决Out of memory:java head space

Tomcat的JVM配置:解决Out of memory:java head space

在最上方位置加入你要为JVM指定内存空间大小的配置:

Tomcat的JVM配置:解决Out of memory:java head space

    Tomcat的JVM配置:解决Out of memory:java head space

四个参数分别代表:

-Xms JVM初始分配的堆内存
-Xmx JVM最大允许分配的堆内存,按需分配
-XX:PermSize JVM初始分配的非堆内存
-XX:MaxPermSize JVM最大允许分配的非堆内存,按需分配

按照需求来分配就可以了,但是又发现一种情况让我非常无奈,tomcat7.0 有两种版本:一种是如上所述,使用startup.bat来开启,用shutdown.bat来关闭的,大多数的都是这个版本,包括我自己的电脑也是。但是!在客户的服务器上tomcat的bin文件夹下是这样的:

Tomcat的JVM配置:解决Out of memory:java head space

Tomcat的JVM配置:解决Out of memory:java head space

    这就非常尴尬了,没有catalina.bat文件,要怎么配置JVM内存?

    好吧,经过两个多小时的百度,终于在一篇不太起眼的大神文章中找到了解决办法:利用注册表来修改JVM内存配置,来看看具体步骤吧:

    首先用命令方式打开注册表:


Tomcat的JVM配置:解决Out of memory:java head spaceTomcat的JVM配置:解决Out of memory:java head space


Tomcat的JVM配置:解决Out of memory:java head space

Tomcat的JVM配置:解决Out of memory:java head space

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初始分配的堆内存:

Tomcat的JVM配置:解决Out of memory:java head space

Tomcat的JVM配置:解决Out of memory:java head space

    同样的方法设置jvmMx最大允许分配的堆内存,分配PermSize和MaxPermSize右键编辑options:

Tomcat的JVM配置:解决Out of memory:java head space

Tomcat的JVM配置:解决Out of memory:java head space


    这样重启tomcat后在上述页面中查看JVM内存状态,就会发现内存已经变更成功了!