最近游戏在腾讯平台上线,已经快破200组,以前是一台机器可能就放一两组游戏区内存绰绰有余,只要没发生内存泄漏,也就没去关心。
但现在为了节约成本,我们会尽量一台虚拟机上放尽量多的游戏区。所以我们默认放了4个区(每个区占用6G,共24G),还有放了logger 服,gate,flash,redis等应用,
发现只要服务器维护后快一星期左右,内存占用从原先的10几个G一路高歌猛进,直到占满内存 95%+,经常性收到监控邮件,内存又不足了,甚至引发OOM killer。
为什么人数这么少了,内存占用还是会达到6G+,垃圾回收怎么没任何作用?
原因是因为即使JVM 从系统中申请来内存后,就不会再归还给系统,向系统交还内存和申请内存都是比较耗时的,或者说,JVM和操作系统之间的通信是比较昂贵的,从另外一个角度来看, 如果JVM一有空闲内存就交给操作系统的话,那必然会存在内存紧张,需要频繁的向操作系统申请内存,现有的做法都是向操作系统申请一大块内存,自己管理,从而避免了频繁的操作系统调用,并且能使用适合自身应用场景,特殊的内存管理方式来提高性能。
所以被JVM GC掉和内存返回给操作系统是两码事,被GC掉只是不占用java heap的空间,JVM会保留该空间给后续对象分配,不会返回给系统,即使是C语言也是这样,malloc分配空间后直接free,空间也不会返回,因为应用有很大的可能继续要求分配空间,绝大部分的实现会保留free 后的内存空间。
如果真想回收给系统也是做到的,JVM有个参数决定空闲堆内存大于一定百分比时才会把内存交还给操作系统,慎用!
想想原因也很简单,如1台24G内存的CVM,每个区我们一开始会分配6G的JVM堆栈空间(xmn,xmx=6g), 懂的人知道,这个JVM一直跑着,会消耗掉6G+的内存占用。但游戏到了第二天后其实也就单区200~300左右在线,完全没必要分配这么多的内存,3G已然足够,但我们又不可能去停机维护修改堆内存占用。所以我们可以在每次维护的时候调整内存占用,于是我给出了个3/8公式,聪明的运维同学帮忙做了脚本实现:
当然了,我们还可以对平均在线,最高在线分配不同的权重去获取应该分配的内存,
这样,内存占用高的问题就基本解决了,资源成本省下了许多。
2015/12/11 楼上做的和说的有两点不同意。第一,根据观察下来,如果xms=6G xmx=6G,实际上JVM并不是一开始就把6G申请进内存,也是渐渐的申请进来,直到6G(所以一般都建议-Xms,-Xmx值一样大,一种说法是为了避免要用到时才临时从系统中申请内存,另一种说法是 避免在每次GC 后调整堆的大小(这个还存在疑问,感觉总堆内存只会增大不会变小),这个说法本来就是不对的,不知道有多少人被欺骗了),
第二点,每次维护的时候做内存配置调整时间上太不灵活,其实1天后就可以做调整,但又要保证JVM不重启,所以我们最好把释放内存放在JVM运行态,Ali JVM就做到了这一点,在FULLGC 的时候做内存释放。
坤谷给我的回复如下:
jvm最初只是和linux要连续6g地址空间,发生写入,产生缺页中断,linux才真的分物理内存。阿里jvm不用的页,jvm是通过madvice告诉Linux,这些页可以考虑回收。linux在自己方便的时候回收。linux下就是用到才分配内存,其他操作系统不熟悉。