在从事 C++ 项目三年后,可执行文件已增长到 4 MB。我想看看所有这些空间的去向。有没有工具可以报告最大的太空猪是什么?很高兴看到按类(类中的所有函数)、按模板(所有实例化)和按库(有多少属于 C 标准库和 STL?exe 中的每个库有多少?)
编辑:注意,我在 Windows 上使用 Visual C++。
在 Linux中,您可以使用nm
显示可执行文件中的所有符号并按大小以相反的顺序对它们进行排序:
$ nm -CSr --size-sort <exe>
选项:
-C
对 C++ 名称进行解码。-S
显示符号的大小。--size-sort
按大小对符号进行排序。-r
反转排序。如果你想获得每个命名空间或每个类的结果,你可以只grep
输出 ' namespace::
'、' namespace::class_name::
'等。
如果您只想查看在可执行文件中定义的符号(而不是在其他地方定义的符号,例如在库中),请添加--defined-only
. 不过,按大小排序应该注意这一点,因为未定义的符号不会有大小。
对于 Windows,您应该仍然可以nm
在二进制文件上使用,因为nm
支持COFF二进制文件。你可以nm
通过 cygwin 安装,或者你可以将你的 windows 可执行文件复制到一个 linux 机器上并nm
在那里运行。
你也可以试试dumpbin
,它会在 Windows 上转储有关二进制文件的信息。您可以使用/SYMBOLS
开关获取有关符号的信息,但它看起来并不直接提供有关其大小的信息。
在 Visual Studio 编译下的 Windows 中,此信息位于您的 .map 文件中(它将位于 .pdb 附近)。
添加:要将 .map 文件中的修饰名称转换为更易于阅读的名称,您可以使用Visual Studio 附带的undname.exe实用程序。它在命令行上接受单个名称,或者您可以为其提供 .map 文件。
例如,
Microsoft (R) C++ Name Undecorator
Copyright (C) Microsoft Corporation. All rights reserved.
Undecoration of "?push_back@?$mini_vector@U?$Point@U?$FixedPoint@$0O@H@Math@@@Math@@$05@@QAAXABU?$Point@U?$FixedPoint@$0O@H@Math@@@Math@@@Z" is
"public: void __cdecl mini_vector<struct Math::Point<struct Math::FixedPoint<14,int> >,6>::push_back(struct Math::Point<struct Math::FixedPoint<14,int> > const &)"
我无法nm
为我工作,但确实设法找到了一个名为Sizer的有用工具。它使用调试接口访问库读取 Visual Studio 创建的调试信息。正如网站上描述的那样,使用起来非常简单。
Sizer.exe <path-to-exe-file>
。输出将转到标准输出,因此您可能希望重定向到文件。代码大小分为不同的部分,并按功能、数据、类等分组,每个部分按代码大小的降序排列。
获取链接图,或用于dumpbin
获取符号和大小的列表。
很可能有很多你并不需要的东西被拉进来。
补充:你得到满意的答案了吗?我意识到人们有两种方法可以解决这样的问题:
我个人更喜欢后者——它更快地得到结果。
你说应用程序是 4MB。假设真正必要的大小是 1MB(或一些这样的大小)。这意味着如果您从地图文件中随机选择一个例程,那么它有 75% 的可能性是您不需要的。找出导致它被包含在内的原因,看看你是否真的需要它。
在您给出的示例中,您看到了一个包装与设备无关的位图的类。您可以在您的应用程序中找到该类的实例,并可能将它们替换为基本的 WIN32 位图。它会不那么漂亮,但会节省大量的应用程序大小。
然后继续做。你去掉的每一块大块都会使剩余的块占据应用程序的更大比例,因为应用程序已经缩小了,但块没有。这使它们更容易在地图文件中找到。
不要只看代码——资源很容易导致数兆字节的增长。