前情:8月1日到新公司入职,到职第一天上级就分配了任务,优化OA。
OA目前的情况:运行一段时间后就会很卡,打开流程或表单一直处于刷新状态,只有重启tomcat服务后才能恢复正常。开始想从oracle数据库入手,确认是不是oracle数据量太大未做优化导致的,导出awr报表做分析,结果实在是看不太懂,只好放弃,转从tomcat入手。
tomcat服务器信息:
系统版本 | CPU核数 | 内存容量 |
---|---|---|
Red Hat 4.8.5-4 | 20 | 64G |
jdk信息:
version | SE RunTIme Environment | HotSpot |
---|---|---|
1.6.0-31 | build 1.6.0_31-b04 | 64-Bit Server VM (build 20.6-b01, mixed mode) |
tomcat信息:
版本 |
---|
6.0.35 |
公司目前在职员工8000人左右,同时在线人数2500人左右。
首先查看原来的JVM设置:
JAVA_OPTS="
-server
-Xms16000m
-Xmx16000m
-XX:PermSize=1024M
-XX:MaxNewSize=1024m
-XX:MaxPermSize=1024m
-Djava.awt.headless=true "
可以看出配置了堆的大小16000M,大约15.5G左右,最大年轻代1G,永久堆内存1G,我们通过VisualVM可以更直观的看到整个内存的分配情况:
注:这是后面模拟当时的服务器信息的截图,调优时没有截图。
通过查询资料及百度然后对JVM做了第一次优化:
JAVA_OPTS="
-server
-Xms16000M
-Xmx16000M
-Xss256k
-XX:NewSize=2048M
-XX:MaxNewSize=2304M
-XX:PermSize=2048M
-XX:MaxPermSize=2304M
-XX:+AggressiveOpts
-XX:+UseBiasedLocking
-XX:+DisableExplicitGC
-XX:+UseParNewGC
-XX:+UseConcMarkSweepGC
-XX:MaxTenuringThreshold=31
-XX:+CMSParallelRemarkEnabled
-XX:+UseCMSCompactAtFullCollection
-XX:LargePageSizeInBytes=128m
-XX:+UseFastAccessorMethods
-XX:+UseCMSInitiatingOccupancyOnly
-Duser.timezone=Asia/Shanghai
-Djava.awt.headless=true
-Dcom.sun.management.jmxremote
-Djava.rmi.server.hostname=192.168.0.99
-Dcom.sun.management.jmxremote.port=8010
-Dcom.sun.management.jmxremote.ssl=false
-Dcom.sun.management.jmxremote.authenticate=false"
xms和xmx没有做改动,还是16000M,增加了xss:每个线程的堆栈大小,加大了年轻代的内存,设置垃圾回收机制CMS,其他参数就不一一解释了,我们看看优化后的情况:
可以看到eden区明显的增大了,old区缩小了一些,perm gen(永久代)有增加,下面多了一列Histogram,这是-XX:MaxTenuringThreshold=31 参数设置后的效果,表示survivor的存活生命周期。这次优化后并没有达到效果,下午访问量大的时候old区很快就满了,然后OA打开流程或表单一直在刷新,要保存的数据也没法保存。
虽然失败了,但是经错这次优化发现了OA访问卡死的原因:old即年老代内存满了,触发了Full GC,然后就是大家说的stop the world,所有线程等待Full GC完成后才能重新访问,自然OA无法访问甚至直接客户端浏览器卡死。既然知道原因了那就对症下药。又是一通百度查寻各种网上的方案。
既然是年老代满了那么就加大年老代不就行了,通过事实验证只是加大年老代是不行的,因为总有一天它还是会满的,那有没有方案能在它满之前就先执行垃圾回收,释放内存呢?答案是肯定可以的。通过配置-XX:CMSInitiatingOccupancyFraction=73可以设置当年老区满多少的时候进行垃圾回收,这里我设置的73%,并不时随便设置的,通过这个公式可以计算出来:
CMSInitiatingOccupancyFraction <=((Xmx-Xmn)-(Xmn-Xmn/(SurvivorRatior+2)))/(Xmx-Xmn)*100
查询资料还发现,可以通过加大survivor区来达到一些对象在未到达年老代就被回收掉,可以通过设置-XX:SurvivorRatio=3参数来达到效果,然后再一次进行优化:
JAVA_OPTS="
-server
-Xms20480M
-Xmx20480M
-Xss256k
-XX:NewSize=5120M
-XX:MaxNewSize=5120M
-XX:PermSize=256M
-XX:MaxPermSize=256M
-XX:SurvivorRatio=3
-XX:+AggressiveOpts
-XX:+UseBiasedLocking
-XX:+DisableExplicitGC
-XX:+UseParNewGC
-XX:+UseConcMarkSweepGC
-XX:+CMSParallelRemarkEnabled
-XX:+UseCMSCompactAtFullCollection
-XX:CMSFullGCsBeforeCompaction=0
-XX:+CMSClassUnloadingEnabled
-XX:LargePageSizeInBytes=128m
-XX:+UseFastAccessorMethods
-XX:+UseCMSInitiatingOccupancyOnly
-XX:CMSInitiatingOccupancyFraction=73
-XX:SoftRefLRUPolicyMSPerMB=0
-verbose:gc
-XX:+PrintGCTimeStamps
-XX:+PrintGCDetails
-Xloggc:/usr/local/tomcat/temp/gc.log
-Duser.timezone=Asia/Shanghai
-Djava.awt.headless=true
-Dcom.sun.management.jmxremote
-Djava.rmi.server.hostname=192.168.0.99
-Dcom.sun.management.jmxremote.port=8010
-Dcom.sun.management.jmxremote.ssl=false
-Dcom.sun.management.jmxremote.authenticate=false"
这次显示增大了整个堆内存,然后增加了survivor的比例,设置了CMS垃圾回收在73%时自动回收,这时服务器的最终效果如下:
通过设置-Xloggc:/usr/local/tomcat/temp/gc.log,可以查看到GC的相关日志。
这次优化后跟踪了2天,占时没出现old区满的情况,当达到73%时会自动执行垃圾回收,由于是并行的,所以对整个OA的访问并不影响。这样的优化应该还有改进的地方,后面我会继续跟踪并优化。