原文出处: 陈斌的博客(@二狗陈Chen) 最近游戏已上线运营,进行服务器内存优化,发现一个非常奇妙的问题,我们的认证服务器(AuthServer)负责跟第三方渠道SDK打交道(登陆和充值),由于采用了curl阻塞的方式,所以这里开了128个线程,奇怪的是每次刚启动的时候占用的虚拟内存在2.3G,然后每次处理消息就增加64M,增加到4.4G就不再增加了,由于我们采用预分配的方式,在线程内部根本没有大块分内存,那么这些内存到底是从哪来的呢?让人百思不得其解。 1.探索一开始首先排除掉内存泄露,不可能每次都泄露64M内存这么巧合,为了证明我的观点,首先,我使用了valgrind。
然后启动测试,跑至内存不再增加,果然valgrind显示没有任何内存泄露。反复试验了很多次,结果都是这样。 在多次使用valgrind无果以后,我开始怀疑程序内部是不是用到mmap之类的调用,于是使用strace对mmap,brk等系统函数的检测:
其结果如下:
我检查了一下trace文件也没有发现大量内存mmap动作,即便是brk动作引起的内存增长也不大。于是感觉人生都没有方向了,然后怀疑是不是文件缓存把虚拟内存占掉了,注释掉了代码中所有读写日志的代码,虚拟内存依然增加,排除了这个可能。 2.灵光一现后来,我开始减少thread的数量开始测试,在测试的时候偶然发现一个很奇怪的现象。那就是如果进程创建了一个线程并且在该线程内分配一个很小的内存1k,整个进程虚拟内存立马增加64M,然后再分配,内存就不增加了。测试代码如下:
其运行结果如下图,刚开始时,进程占用虚拟内存14M,输入0,创建子线程,进程内存达到23M,这增加的10M是线程堆栈的大小(查看和设置线程堆栈大小可用ulimit –s),第一次输入1,程序分配1k内存,整个进程增加64M虚拟内存,之后再输入2,3,各再次分配1k,内存均不再变化。 这个结果让我欣喜若狂,由于以前学习过谷歌的Tcmalloc,其中每个线程都有自己的缓冲区来解决多线程内存分配的竞争,估计新版的glibc同样学习了这个技巧,于是查看pmap $(pidof main) 查看内存情况,如下: 请注意65404这一行,种种迹象表明,这个再加上它上面那一行(在这里是132)就是增加的那个64M)。后来增加thread的数量,就会有新增thread数量相应的65404的内存块。 |