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

简介: 对页游《小兵大战》服务器DM内存溢出的排错过程总结
这篇文章成文于2012年公司内存论坛交流,现和大家分享一下!--流子

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

背景

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

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

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

模式:大区单服

游戏服务器硬件配置

资源类型 硬件 型号/版本/配置
VC3 CPU 8核,相当于2.0GHZ的Xeon处理器或Opteron处理器
内存 7G 相当于DDR3 1066MHZ
硬盘 300G SATA raid0 结构单盘硬盘
OS Linux 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

 

目录
相关文章
|
11天前
|
存储 缓存 安全
阿里云服务器内存型r7、r8a、r8y、r8i实例区别及选择参考
随着阿里云2024年金秋云创季的开始,目前在阿里云的活动中,属于内存型实例规格的云服务器有内存型r7、内存型r8a、内存型r8y和内存型r8i这几个实例规格,相比于活动内的经济型e和通用算力型u1等实例规格来说,这些实例规格等性能更强,虽然这几个实例规格的云服务器通常处理器与内存的配比为都是1:8,但是他们在处理器、存储、网络、安全等方面等性能并不是一样的,所以他们的适用场景也有着不同。本文为大家介绍内存型r7、r8a、r8y、r8i实例的性能、适用场景的区别以及选择参考。
|
18天前
|
存储 关系型数据库 MySQL
查询服务器CPU、内存、磁盘、网络IO、队列、数据库占用空间等等信息
查询服务器CPU、内存、磁盘、网络IO、队列、数据库占用空间等等信息
191 2
|
4天前
|
存储 缓存 内存技术
为什么服务器内存硬件上的黑色颗粒这么多?
为什么服务器内存硬件上的黑色颗粒这么多?
|
1月前
|
存储 弹性计算 算法
前端大模型应用笔记(四):如何在资源受限例如1核和1G内存的端侧或ECS上运行一个合适的向量存储库及如何优化
本文探讨了在资源受限的嵌入式设备(如1核处理器和1GB内存)上实现高效向量存储和检索的方法,旨在支持端侧大模型应用。文章分析了Annoy、HNSWLib、NMSLib、FLANN、VP-Trees和Lshbox等向量存储库的特点与适用场景,推荐Annoy作为多数情况下的首选方案,并提出了数据预处理、索引优化、查询优化等策略以提升性能。通过这些方法,即使在资源受限的环境中也能实现高效的向量检索。
|
11天前
|
弹性计算
阿里云2核16G云服务器多少钱?亲测ECS内存型r8i租赁价格
阿里云2核16G云服务器,内存型r8i实例1年6折优惠后价格为1901元,月付334.19元,按小时计费0.696221元。更多配置及优惠详情,请访问阿里云ECS页面。
|
2月前
|
分布式计算 大数据 数据挖掘
阿里云服务器计算型c8i、通用型g8i、内存型r8i实例测评与价格参考
阿里云服务器计算型c8i、通用型g8i、内存型r8i实例是阿里云的第八代云服务器实例规格,是除了计算型c7和c8y、通用型g7与g8y、内存型r7与r8y之外同样深受用户喜欢的云服务器实例规格。本文将详细介绍阿里云第八代云服务器中的计算型c8i、通用型g8i、以及内存型r8i实例,包括它们的技术特性、适用场景以及最新的活动价格信息。
阿里云服务器计算型c8i、通用型g8i、内存型r8i实例测评与价格参考
|
1月前
|
存储 机器学习/深度学习 应用服务中间件
阿里云倚天云服务器实例:计算型c8y、通用型g8y、内存型r8y实例介绍
阿里云倚天云服务器是基于阿里云自研的倚天710 ARM架构CPU打造的高性能计算产品系列,它依托先进的第四代神龙架构,旨在为用户提供稳定可预期的超高效能体验。倚天云服务器在存储、网络性能及计算稳定性方面实现了显著提升,主要得益于其芯片级的快速路径加速技术。本文将深度解析阿里云倚天云服务器的计算型c8y、通用型g8y、内存型r8y实例,探讨其优势及适用场景,以供选择参考。
|
22天前
|
存储 分布式计算 安全
阿里云服务器内存型r7、内存型r8y、内存型r8i实例规格性能对比与选择参考
在选择阿里云服务器实例规格时,针对内存密集型应用和数据库应用,内存型r7、内存型r8y和内存型r8i实例是这部分应用场景选择最多的热门实例规格。为了帮助大家更好地了解这三款实例的区别,并为选择提供参考,本文将详细对比它们的实例规格、CPU、内存、计算、存储、网络等方面的性能,并附上活动价格对比。让大家了解一下他们之间的不同,以供参考选择。
|
2月前
|
存储 关系型数据库 MySQL
查询服务器CPU、内存、磁盘、网络IO、队列、数据库占用空间等等信息
查询服务器CPU、内存、磁盘、网络IO、队列、数据库占用空间等等信息
128 5
|
1月前
|
弹性计算 关系型数据库 数据安全/隐私保护
阿里云国际版如何配置Windows服务器的虚拟内存
阿里云国际版如何配置Windows服务器的虚拟内存