减少不熟悉的大型代码库的内存占用
假设我们有一个相当大的(〜2.2 MLOC),相当老的(开始于10年前)的C / C ++ Windows桌面应用程序。大约10%的模块是外部模块,没有源,只有调试符号。
我们将如何将应用程序的内存占用减少一半?至少,我们将怎么做才能找出消耗内存的地方?
解决方案
我认为问题提出得不好。
源代码的大小与内存占用量没有直接关系。当然,编译后的代码将占用一些内存,但是应用程序可能会自己拥有内存需求。静态(代码中声明的变量)和动态(应用程序创建的对象)。
我建议我们分析程序执行情况并仔细研究代码。
这不是一件容易的事。首先追踪我们可以找到的所有内存泄漏(一个好的工具是Rational Purify)。浏览源代码并尝试优化数据结构和/或者算法。
抱歉,这听起来有些悲观,但是将内存使用量减少50%听起来并不现实。
使用包装器覆盖malloc()/ free()和new()/ delete(),以跟踪分配的大小以及分配的来源(通过记录调用栈,然后在符号表中解析)。关闭时,让包装器显示仍分配的所有内存。
这应该使我们既可以确定最大的分配位置,又可以发现任何泄漏。
这是内存跟踪应用程序的描述/骨架,我用来将我们的游戏的内存消耗减少了20%。它帮助我跟踪了外部模块完成的许多分配。
对我而言,首先要开始的地方是:
应用程序会做很多预分配内存以供以后使用吗?这种记忆是否经常摆放在未使用过,从未分发过的地方?考虑根据需要切换到更新/删除(或者最好使用smart_ptr)。
代码是否使用静态数组,例如
Object arrayOfObjs[MAX_THAT_WILL_EVER_BE_USED];
并在此数组中分发objs?如果是这样,请考虑手动管理此内存。
我们有机会很快发现一些重大的低效率现象。首先,我们应该检查内存的用途。我发现一个非常方便的工具是Memory Validator
一旦有了此"内存使用情况图",就可以检查"悬空水果"。是否有任何数据结构消耗大量内存,并且可以用更紧凑的形式表示?特别是这通常是可能的。当数据访问被很好地封装时,并且当我们有空闲的CPU能力时,我们可以专门在每次访问时对它们进行压缩/解压缩。
用于内存使用情况分析的工具之一是LeakDiag,可以从Microsoft免费下载。显然,它允许将所有用户模式分配器挂接到VirtualAlloc,并可以随时将进程分配快照转储到XML。然后,可以使用这些快照确定哪些调用堆栈分配了最多的内存以及哪些调用堆栈正在泄漏。它缺少用于快照分析的漂亮前端(除非我们可以通过Microsoft Premier Support获得LDParser / LDGrapher),但是所有数据都在那里。
还有一点要注意的是,由于缓存,我们可能会从BSTR分配器中得到错误的泄漏肯定信息,请参阅"嘿,为什么我泄漏了所有的BSTR?"