对页游《小兵大战》服务器DM内存溢出的排错过程总结

Posted 流子

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了对页游《小兵大战》服务器DM内存溢出的排错过程总结相关的知识,希望对你有一定的参考价值。

这篇文章成文于2012年公司内存论坛交流,现和大家分享一下!--流子

这个事件发生在前一阵子,总结为了给项目组的同事和自己遇到类似问题时少走冤枉路,定位问题的思路如果能给你的工作带来一点点帮助,我会非常开心!

背景

项目名《小兵大战》  欢迎试玩,-->传送门<--

合作平台:腾讯QQ空间,朋友网,新浪微游戏等

托管模式:使用腾讯云平台托管/部署服务(hosting) 或者自己出服务器

模式:大区单服

游戏服务器硬件配置

资源类型硬件型号/版本/配置
VC3CPU8核,相当于2.0GHZ的Xeon处理器或Opteron处理器
内存7G 相当于DDR3 1066MHZ
硬盘300G SATA raid0 结构单盘硬盘
OSLinux 2.6 (suse 10,64位)

跑的都是JAVA应用(JDK 1.6),用的数据库为mysql5.5.13,缓存数据库为Redis 2.6.11

事件:

19:35 事件信息:腾讯云监控-系统内存将占满

手机收到消息【腾讯云监控,系统内存将占满】app100630301|19:35|10.190.*.*|系统内存使用达到95%,剩余0.35G【腾讯科技】

19:50  事件信息:通过跳板机登录不了游戏服务器

怀疑整台虚拟机宕掉,通过腾讯运维重启解决

21:37 事件信息:应用游戏逻辑 game server和gate server停掉

重启所有应用解决,并开始监控分析

分析: 

step 1. 查看服务器应用的日志,没有抛出任何异常,有点诡异!

Step 2. 莫非是JVM crash 了,这样的话,应该能在几个应用的当前目录下能找到hs_err_pid*******.log 的crash log.,也没有找到,迷茫!

step 3 查看系统日志

app100630301@Tencent64:~>cat /var/log/messages |grep -i "killed process"

<span style="color:#000066">Jun 28 21:36:08 VM_235_15 kernel:out of memory:killed process [pid](java)
................

................</span>

sudo dmesg|grep -i kill|less

[6710782.021013] java invoked oom-killer: gfp_mask=0xd0, order=0, oom_adj=0, oom_scoe_adj=0
[6710782.070639] [<ffffffff81118898>] ? oom_kill_process+0x68/0x140 
[6710782.257588] Task in /LXC011175068174 killed as a result of limit of /LXC011175068174 
[6710784.698347] Memory cgroup out of memory: Kill process 215701 (java) score 854 or sacrifice child 
[6710784.707978] Killed process 215701, UID 679, (java) total-vm:11017300kB, anon-rss:7152432kB, file-rss:1232kB

以上表明,对应的java进程被系统的OOM Killer给干掉了,得分为854.

解释一下OOM killer(Out-Of-Memory killer),该机制会监控机器的内存资源消耗。当机器内存耗尽前,该机制会扫描所有的进程(按照一定规则计算,内存占用,时间等),挑选出得分最高的进程,然后杀死,从而保护机器。

dmesg日志时间转换公式:
log实际时间=格林威治1970-01-01+(当前时间秒数-系统启动至今的秒数+dmesg打印的log时间)秒数:

date -d "1970-01-01 UTC `echo "$(date +%s)-$(cat /proc/uptime|cut -f 1 -d' ')+12288812.926194"|bc ` seconds"

内存总占用超过系统内存,然后系统优先kill掉相对耗内存的进程,这种情况下既不会有crash log,也不会有core dump,推断Game server和gate server就是这样被OOM killer 灭掉的!定位到Game server 或Gate server有内存泄漏。

step 4 开始监控重启后的应用,发现gate 的内存占用有问题!

app100630301@Tencent64:~>t:~ # ps -ef|grep java

root     25936     1  0 Apr19 ?        00:17:29 java -server -Xms1G -Xmx1G -Xmn256m -XX:PermSize=64m -XX:MaxPermSize=64m -XX:+UseParNewGC -XX:+UseConcMarkSweepGC -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+PrintGCApplicationStoppedTime -XX:+HeapDumpOnOutOfMemoryError -Xloggc:gate_gc_1_201304191211.log -jar /data/ztgame/S20120712R1/gate/gate.jar 1

app100630301@Tencent64:~>top p 18202

  PID USER      PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  COMMAND 
 25936 root      18   0 4359m 492m 7856 S    0  64.0  17:29.21 java 

进程占用了4G多,单个进程占用达到了60%多(总共7G) 问题定位到了进程gate 以前也遇到过内存溢出的情况,但应用都会抛出java.lang.OutOfMemoryException,而这次却没有,但为何 gate苑苑超出了JVM内存配置的2G的上限呢?

Step 5 查看堆的占用

app100630301@Tencent64:~>jmap -heap 18202

<span style="color:#330099">Heap Configuration:
   MinHeapFreeRatio = 40
   MaxHeapFreeRatio = 70
   MaxHeapSize      = 1073741824 (1024.0MB)
   NewSize          = 268435456 (256.0MB)
   MaxNewSize       = 268435456 (256.0MB)
   OldSize          = 5439488 (5.1875MB)
   NewRatio         = 2
   SurvivorRatio    = 8
   PermSize         = 67108864 (64.0MB)
   MaxPermSize      = 67108864 (64.0MB)

Heap Usage:
New Generation (Eden + 1 Survivor Space):
   capacity = 241631232 (230.4375MB)
   used     = 43095400 (41.098976135253906MB)
   free     = 198535832 (189.3385238647461MB)
   17.835194417251493% used
Eden Space:
   capacity = 214827008 (204.875MB)
   used     = 41506656 (39.583831787109375MB)
   free     = 173320352 (165.29116821289062MB)
   19.320967315245575% used
From Space:
   capacity = 26804224 (25.5625MB)
   used     = 1588744 (1.5151443481445312MB)
   free     = 25215480 (24.04735565185547MB)
   5.927215053866137% used
To Space:
   capacity = 26804224 (25.5625MB)
   used     = 0 (0.0MB)
   free     = 26804224 (25.5625MB)
   0.0% used
concurrent mark-sweep generation:
   capacity = 805306368 (768.0MB)
   used     = 265440944 (253.1442108154297MB)
   free     = 539865424 (514.8557891845703MB)
   32.96148578325907% used
Perm Generation:
   capacity = 67108864 (64.0MB)
   used     = 26712504 (25.47502899169922MB)
   free     = 40396360 (38.52497100830078MB)
   39.80473279953003% used</span>


算了下,实际堆内存占用才1G左右,苑苑没有达到JVM的内存占用上限,那么,那3G多的内存是从哪里泄漏的呢?

Step 6. JVM除了堆内存外,就只有栈内存和Direct Memory了,栈空间每个线程是固定的,而占用大小基本固定而且可以忽略,而Direct Memory或者叫直接内存,并不是虚拟机运行时数据区的一部分,不受JVM的GC管理,其中Java的nio中Buffer直接在堆外分配的就属于这个部分,直接以native的方式分配内存,这是为了提高网络和文件的IO效率,避免过度的内存拷贝而出现的,那么,怀疑的目标就只剩下Direct Memory了。

Direct Memory 占用的大小没有直接的工具或者API可以查看,不过,我们可以通过反射在某个类里拿到这个数据。

<span style="color:#000066">Class<?> c = Class.forName("java.nio.Bits");
Field maxMemory = c.getDeclaredField("maxMemory");
maxMemory.setAccessible(true);
Field reservedMemory = c.getDeclaredField("reservedMemory");
reservedMemory.setAccessible(true);
Long maxMemoryValue = (Long)maxMemory.get(null);
Long reservedMemoryValue = (Long)reservedMemory.get(null);</span>

如果不想反射获得,那么,netty已经给我们提供了一个方法,

io.netty.util.internal.PlatformDependent#maxDirectMemory()直接用就行,感谢netty

参考来源:JVM的DirectMemory设置

reservedMemoryValue 表示直接内存的已占用大小,结果明显正式了我的猜测!Direct Memory增长失控了,那么,是什么导致了Direct Memory内存泄漏呢?

Step 7.记得为了减少和客户端的数据通信量大小,协议的来往都会通过zlib进行压缩和解压,而 zlib codec为了提高压缩和解压的效率采用了nio包的direct-buffers.那么,如果猜测是对的话,如果暂时关闭压缩和解压的功能,应该停止内存泄漏,于是吧服务器的压缩开关关闭。

<span style="color:#000066">14:06 400M 5.6%
14:10 511M,7.3% 
15:08 606M 8.7%
15:14 626M 8.9%
16:17 629M 9.0% </span>

基本已经不变,稳定!

Step 8 知道了根本原因,修改代码,zlib codec的Deflater 和Inflater在压缩和解压后都需要关闭,才能保证内存不会泄漏!

<span style="color:#000066">Inflater decompresser=null;
解压加上 finally
decompresser.end();

Deflater compresser=null;
压缩加上 
解压加上 finally
compresser.end();
</span>

Step 9.修改完毕,发出版本,搞定!

总结


综上,分析问题的定位思路,从确定应用Game server或者gate server --> 锁定 Gate server -> Gate Server -> 锁定Direct Memory的溢出->最终确定是Zlib的压缩和解压为内存溢出的根本原因,层层分析,顺藤摸瓜,希望大家看到后都能得到一些收获!

有什么问题想交流的,请随时和我联系,谢谢!

QQ:41157121 


 

以上是关于对页游《小兵大战》服务器DM内存溢出的排错过程总结的主要内容,如果未能解决你的问题,请参考以下文章

五种内存溢出案例总结:涵盖栈深度溢出永久代内存溢出本地方法栈溢出JVM栈内存溢出和堆溢出

Java运行时数据区域划分

记一次线上内存溢出问题排查过程

Java 内存溢出(java.lang.OutOfMemoryError)的常见情况和处理方式总结

jenkins内存溢出的一些解决过程

java虚拟机java内存区域与内存溢出异常