Chinaunix首页 | 论坛 | 博客
  • 博客访问: 4972819
  • 博文数量: 1696
  • 博客积分: 10870
  • 博客等级: 上将
  • 技术积分: 18357
  • 用 户 组: 普通用户
  • 注册时间: 2007-03-30 15:16
文章分类
文章存档

2017年(1)

2016年(1)

2015年(1)

2013年(1)

2012年(43)

2011年(17)

2010年(828)

2009年(568)

2008年(185)

2007年(51)

分类: LINUX

2010-03-20 00:28:02

捉虫记------之内存泄漏
作者:任自流
     Linux 可以说是OS领域中的后起之秀了。随着其应用的广泛推广,越来越多的人把目光投向这块风水宝地。
本文试图从一个程序员的角度对LINUX 编程中的内存泄漏作一个探索。当然,以JAVA为生的兄弟们有了垃圾
回收器这个法宝,似乎可以高枕无忧了。所以本文就以C/C++为例,而 EMACS/VI 、GCC、GDB堪称这方面的三
剑客。另外允许我假设您的编程及调试环境已经完备。如果您用到的工具还没有安装的话,就去央求 一下网管
大哥吧。如果您很要强,说我要自己搞定。那岂不是砸了别人的饭碗,怯以为是不可取的(声明一点,我丝毫
没有鄙视网管大哥的意思。 相反,对能熟练搭建数百个节点的集群的网络工程师们,我还是非常崇拜的)。
     好了,就让我们从一个实际的项目开始吧。我的项目是一个LINUX平台上的休闲网络游戏服务器。为了尽量
少给自己添麻烦(毕竟领导要求半个月内完 成),我并没有自己打造内存管理器。当然,有很多现成的内存
管理库可供使用,如果你对它很熟悉,又是内存管理方面的高手。那我就不多说了,毕竟个 人有自己的爱好。
另外,我只是在程序初始化的时候用到了全局的 NEW 来申请必要的内存。其他地方就在也找不到它的影子了。
那在内部需 要内存的地方咋办呢?经过苦思冥想我终于得到一个很傻的策略,用固定的数组或STL的容器来做。
这样服务器一启动,就几乎达到了内存需求的上限。 比较收放自如的动态内存管理,可算的上超级笨了。本
以为这样就可以万事大吉了,可是在压力测试的时候,用 top 工具来监视内存的使用,发现进程占有的物理内存
的数量还是蹭蹭的往上涨。唉,真是怕啥来啥呀!
为了照顾象我一样的菜鸟,我把 top工具的使用方法介绍一下。
首先要找到服务器进程的ID(PID),例如:
[ss@test ss]$ ps -ef | grep gameserver (gameserver是进程的名字)。
输出如下:
ss      2279 17918  0 19:07 pts/28   00:00:00 ./gameserver
ss      2437 17875  0 19:20 pts/19   00:00:00 grep gameserver

其中第一行第二列的 2279 就是该进程的PID。
[ss@test ss]$ top -p 2279
就可以进入监视界面了。 top是一个动态显示过程,即可以通过用户按键来不断刷新当前状态。如果有什么具体问题,可以用 man 命令来打开其帮助文档一看究竟。我们只需要留意的是其中几个显示项目。
SIZE 该进程的代码大小加上数据大小再加上堆栈空间大小的总数。单位是KB。
RSS 该进程占用的物理内存的总数量,单位是KB。
SHARE 该进程使用共享内存的数量, 单位是KB。
如果top工具的版本不同,可能会是其他的显示项目。
VIRT:进程占用的虚拟内存值。 RES:进程占用的物理内存值。 SHR:进程使用的共享内存值。单位是KB。
       该怎么办呢?求别人来检查自己的代码吗,那是不可能的。大家都有自己的工作,这样费力费神的事,没有人
愿意做的。还是靠自己吧(现在想想大学的 老师对我们可真是关怀入微了呀).
     首先使用我们的法宝--内存探测工具来探照一下BUG的大概方位。这方面的工具有很多,各有优缺点。我就拿比较常用的  valgrind 说一下吧。为了使用内存探测,你可以在 valgrind 的命令行中加入 --tool=memcheck 。当然
,也可以不加,因为 memcheck 是默认配置。其他与内存检查有关的标志有:
--leak-check=一个泄漏是指这样一个malloc的块,它还没有被 free掉,但是已经找不到指向它的指针了。由于不存在指向它的
指针,这个块永远得不到释放(当然,进程被杀死后,内核会回收这块内存)。如果设 置为summy,只显示泄漏发生的总数。如果设置为full或yes,会给出每个单独泄漏的详细信息。
--show-reachable= [默认: no]
当设置为no时,内存探测器仅仅报告这样一些块,要么根本找不到指向它们的指针,要么只能找到唯一的指向其中部的指针(注意 这是个野指针)。这些块是最可能存在内存泄漏的。当设置为yes时,探测器另外会报告一些块,尽管探测器能够发现指向这些块的指针。至少原则上,你的程序 可能已经在退出前释放了它们。对比那些没有指针或只有一个内部指针的块就能够发现:它们更有可能提示出内存泄漏,因为你的确无法得到一个指向块首的可以手 工释放的指针,即使你想这样做。
  其他还有很多可选的标志,我就不一一陈述了。你可以参考valgrind的用户手册。
  通过仔细分 析valgrind的内存泄漏报告,发现有一个确定的泄漏发生在一个数据库操作的接口内,接口是由同事提供的。顺便说一句,对于一个比较大的项目,通常是 有几个乃至几十个人分工合作完成的。每个人只是编写其中的部分功能模块,这些模块一般封装到不同的库里。于是我另外编写了一段代码,把这个模块分离出来。 独立测试后。确定这个模块的确存在泄漏,通告写该模块的同事,得到了解决。  事情到现在似乎可以结束了。不幸的是,放到压力测试上内存仍然会有大幅的上 升,而且用户离开后,内存占有也不会减少。只好拿出最后的杀手锏了。沿程序的流程逐个排除可能存在的泄漏。这个比较繁琐,需要耐心和细心。我把代码中不相 干的部分屏蔽(就是用/*  */注释掉),一步一步地来检测每个于内存有关的代码。终于在一个队列上发现了问题,经过反复测试,发现当队列中的元素全部移除后,队列所占有的内存并没 有减少。这是为什么呢?
  我用的是STL的queue模式,而STL向来是推崇速度第一的,所以queue在所有元素移除后,并没有立即释放它 所申请的内存,而是作为备用提供给以后插入的元素。这就是内存占有只升不降的原因了。
  到此为止,终于可以告一段落了。希望您能从我解决问题的 方法中得到点启示。当然,我只是个不如流的程序,难免会有这样那样的错误,望您见谅。同时,我的代码也开始在公测上跑了,但我的心总是惴惴的,怕有这样那 样的问题。唉,这也许是一个程序员的宿命。

 

鄙 人msn联系方式: (常在线)
阅读(823) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~