关于hibernate导致tomcat内存暴涨,页面反应速度减慢
2012年6月23日
今天追踪一个关于页面内存暴涨,页面响应过慢的问题。花了4个多小时,总算找到问题出在哪了。
一、问题描述
最近我们在一台独享服务器上搭建了Tomcat6.0.19环境,发现访问首页时,内存暴涨,一直不退。每次刷新内存增加近10M。一个人狂刷新一分钟就可以把tomcat搞死机。
然后,找各种办法解决问题。整了几天,每天中午轮流监控Tomcat服务器,发现它挂了后就重启。累坏了。
每天到google上面搜索答案,有人说是程序的问题,程序内存泄露。甚至问到“有没有数据库没有释放”、“有没有使用死循环”、有没有写“System.gc()自动释放内存”、有没有“在Service层查询时使用all()方法,查处了所有记录”。等等。
而我一直不承认程序有问题。理由是:我们曾经将这个程序在租用的2个虚拟主机中用了近1个月。不断维护,最后达到一个星期没有出现一个异常的情况。
二、问题分析(关于tomcat内存溢出问题)
分析Tomcat死机的日志,发现是内存耗尽。
大致有这么一段,PSPermGen total 86016K used 86015K
到网上搜了一下,明白了jvm内存分类。然后配置内存,让tomcat自动回收jvm内存。
当然,配置过程也遇到了很多问题,花了一天多。问题是网上的很多人都是抄来抄去,要在一大堆垃圾中选出精品不容易呀。
配了一天都只起到一定作用。比如:给tomcat设置较大内存后,tomcat可以承受到2.19G。只是再刷新就可以把他整崩溃。
最后,请了几个牛人帮忙,一个牛人凌晨帮我们看tomcat配置。问我们,是否加了,-server参数。最后,我同事加上了-server参数。 然后,回去睡觉了,发现java.exe内存真的能回收了。很多问题也解决了。
我们配置如下:(将tomcat6的安装版卸载,换成绿色版)
在catalina.bat的@echo off下面添加(就是第二行)
set JAVA_OPTS=-server -Xms512m -Xmx1024m -XX:MaxNewSize=512m -XX:MaxPermSize=256m
在startup.bat下面添加(让tomcat的工具自动回收内存)
@echo off
set JAVA_OPTS=%JAVA_OPTS%
-Dcom.sun.management.jmxremote.port=1090
-Dcom.sun.management.jmxremote.ssl=false
-Dcom.sun.management.jmxremote.authenticate=false
-Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager
-Djava.util.logging.config.file="%CATALINA_HOME%\conf\logging.properties"
三、问题分析(关于页面反应速度异常)
我们的首页显示餐馆的信息,主要根据用户选择地区,然后将该地区的餐馆列表和热卖美食列表显示出来。我们发现一个很奇怪的现象,访问某一学校东校区时:
8个餐馆:Action执行2063ms(获取餐馆列表:1220ms + 获取热卖美食:825ms)。
然后换成其他校区分别为:
3个餐馆:Action执行 51ms (获取餐馆列表:29 + 获取热卖美食: 2)
3个餐馆:Action执行 53ms (32 + 1)
4个餐馆:Action执行 90ms (74 + 1)
看到这个数据,我就很纳闷,怎么时间不成比例呢?
于是,我将8个餐馆删掉4个对比。由于有外键关联,我必须要删除其他的很多的。为了删除一个餐馆记录,我必须删除其他记录多达6个表处,可见外键关系较为复杂。
等我删掉后,发现东校区:
4个餐馆:Action执行111ms(获取餐馆列表:53ms + 获取热卖美食:37ms)。
我发现这个变化太明显了吧。然后,我怀疑那四个餐馆关联了太多东西。我查询出来的不仅仅是餐馆,可能连带查询了另外的6个表的记录。
我就想到了是hbm.xml文件中设置lazy = "true"问题。
通过测试,我发现是 餐馆对应订单时,one-to-many时,lazy = "true"。这个就很明显了。
查询一个餐馆,就把它的上百份订单级联查询出来了,同时把所有菜品也级联查询出来了,把所有菜品分类都查询出来了。
而就东校区的这8家餐馆订单较多,其他校区由于刚开业,订单几乎没有。这下就可以解释为什么东校区这么耗时间(当然也耗内存)。
然后,我将hbm.xml的所有lazy="true"全部去掉,恢复数据库数据。
东校区结果如下:
8个餐馆:Action执行25ms(获取餐馆列表:3ms + 获取热卖美食:8ms)。
四、结论:
lazy="false"使用时要慎重。例如:对与1:1的情况,使用直接影响不大,对于1:n情况就要慎重了。尤其是n成百上千时,问题就相当严重了。我建议最好不用。
第一次,配置和管理服务器,收获真大呀。对我以后的编程风格都造成了深远的影响。我会注意服务器的时间和空间效率问题。
当然测试方法比较土。先将服务器正常上线后,我会学着使用JMeter和roadrunner的工具做压力测试,配置好服务器。
忙里偷闲!记录下来!