博客首页 注册 建议与交流 排行榜 加入友情链接         宝宝相册的专门空间
推荐 投诉 搜索: 帮助

水龙卷

我是一个对自己进行debug的程序,所以行动迟缓些,不要见怪
  waterspout.cublog.cn

关于作者
姓名:何云龙
职业:Linux移动终端平台开发
介绍:走的更远些
|| << >> ||
我的分类


性能和内存问题的思考
最近公司在新的产品上遇到了很多critical的问题,包括内存紧张,性能低下,还有随机的crash问题,其实在前一款产品中我们也碰到很多,并且解决了很多,但是在新的产品中引入了几个不重要但是对资源消耗又很大的模块,在硬件没有大的升级的情况下,本来就已经很勉强的系统当然会变得很脆弱。
 
我在前一款产品中除了负责本模块的bug,主要精力是负责查找random crash的原因,由于有测试人员和同事的大力协助,发现了几个关键的crash point,因此他们觉得我似乎比较在行,实际上我能够做的,换了一个人只要坚持去做也一样能做。在解决random crash的问题时,一个因素是对系统逻辑的了解程度,另外一个是解决问题的手段。第一个因素没啥可说,第二个因素往往就成为解决问题的关键了。
 
在Linux上有非常多的工具用来查找这类的问题,Linux本身的一些信息也能够提供给我们线索。比如内存占用过多的问题,通过在运行时监测/proc目录下的status和maps我们可以知道是在什么情况下内存消耗增长了,但是maps不能告诉我们到底这个内存是在哪里分配的,通过重载malloc和new,在必要的时候把调用栈打印出来就可以知道了,但是这需要做很多细致琐碎的工作。glibc实现的malloc是针对申请内存大小不同采取不同的策略的,由此是会造成运行时内存的空洞,调整其阈值会影响内存空洞的大小,同时也会影响performance,而同事发现用BSD的malloc实现是能够避免这个问题,而又把对performance的影响降低到最低限度。但是替换这么关键的实现会不会有什么副作用呢?还不知道。
 
status中的VmData是申请的内存,但是Linux的内存管理并不是申请多少就分配多少,比如创建一个thread就会reserve一定大小的内存用作调用栈,但是没使用的时候这个内存并没有真正被应用程序占据。RSS才是真正被程序使用的内存的大小,但是RSS还包括了代码段。所以maps相对来说信息比较详细,使用mapsparser可以得到更直观的信息。/proc目录下的meminfo里面包含了当前系统的内存状况,其中Free,Buffer和Cache可以认为是总的可用内存。
 
内存很低的时候也会引起performance问题,因为这时kernel的swap daemon会不断的尝试对内存进行整理或交换,尤其是swap空间也很小的时候。
 
内存泄露问题可以使用valgrind来检查,但是现在还没看到嵌入式的版本,必要的时候可以自己仿照valgrind的做法来覆盖malloc和new。但是valgrind是采用动态代码注入的方式来做的,自己实现基本不太可能,可以尝试的是在编译期覆盖。通过维护内存分配的地址和调用关系,我们可以知道哪些内存没有被释放。
 
内存方面最为彻底的做法可能是直接拿掉某些不关键的应用,或者在不关键的时候关闭它。重新启动程序会减少内存空洞,但是仍然要想办法避免给用户的使用带来困扰。
 
性能问题似乎没有太多捷径可走,直观的想法是避免无用的或者低效的操作。valgrind附带的工具callgrind配合kcashgrind能够给出运行时的调用关系并给出每个函数所用的时间。gprof也能够做类似的事情但是需要重新编译程序。在了解程序运行逻辑的情况下进行优化可能是没什么规则,常见的也是最笨的做法是使用cache来避免低效的数据加载释放,但是第一次加载cache仍然会有performance问题,而且后期也会给系统的内存带来负担。
 
crash问题是很有趣的,在Linux上出现的crash有相当多是没什么办法追踪的。要解决crash,需要了解调用栈,需要了解是哪个变量出了错。但是产品的版本里经常是非debug版本的,而且coredump也经常是关掉的,这就进一步增加了解决问题的难度。但是不管怎样,debug也好,coredump也好,打印log也好,往往不是一种手段就能得到足够信息的。
 

发表于: 2008-01-19,修改于: 2008-01-19 08:47,已浏览222次,有评论0条 推荐 投诉


网友评论
 发表评论