iOS APP可执行文件的组成

2014-7-30

iOS APP编译后,除了一些资源文件,剩下的就是一个可执行文件,有时候项目大了,引入的库多了,可执行文件很大,想知道这个可执行文件的构成是怎样,里面的内容都是些什么,哪些库占用空间较高,可以用以下方法勘察:

1.XCode开启编译选项Write Link Map File
XCode -> Project -> Build Settings -> 搜map -> 把Write Link Map File选项设为yes,并指定好linkMap的存储位置
linkmap

2.编译后,到编译目录里找到该txt文件,文件名和路径就是上述的Path to Link Map File
位于~/Library/Developer/Xcode/DerivedData/XXX-eumsvrzbvgfofvbfsoqokmjprvuh/Build/Intermediates/XXX.build/Debug-iphoneos/XXX.build/

这个LinkMap里展示了整个可执行文件的全貌,列出了编译后的每一个.o目标文件的信息(包括静态链接库.a里的),以及每一个目标文件的代码段,数据段存储详情。

1

以伊书项目为例,在LinkMap里首先列出来的是目标文件列表:

# Object files:
[ 0] linker synthesized
[ 1] /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator7.0.sdk/usr/lib/crt1.o
[ 2] /Users/bang/Library/Developer/Xcode/DerivedData/yishu-eyzgphknrrzpevagadjtwpzzeqag/Build/Intermediates/yishu.build/Debug-iphonesimulator/yishu.build/Objects-normal/i386/TKPFileInfo.o
...
[280] /Users/bang/Downloads/yishu/yishu/Classes/lib/UMeng/MobClick/libMobClickLibrary.a(UMANJob.o)
[281] /Users/bang/Downloads/yishu/yishu/Classes/lib/UMeng/MobClick/libMobClickLibrary.a(UMANWorker.o)
[282] /Users/bang/Downloads/yishu/yishu/Classes/lib/UMeng/MobClick/libMobClickLibrary.a(MobClick.o)
[283] /Users/bang/Downloads/yishu/yishu/Classes/lib/UMeng/MobClick/libMobClickLibrary.a(UMANLaunch.o)
...

前面中括号里的是这个文件的编号,后面会用到,像项目里引用到静态链接库libMobClickLibrary.a里的目标文件都会在这里列出来。

2

接着是一个段表,描述各个段在最后编译成的可执行文件中的偏移位置及大小,包括了代码段(__TEXT,保存程序代码段编译后的机器码)和数据段(__DATA,保存变量值)

# Sections:
# Address   Size     Segment   Section
0x00002740 0x00273890 __TEXT __text
0x00275FD0 0x00000ADA __TEXT __symbol_stub
0x00276AAC 0x00001222 __TEXT __stub_helper
0x00277CCE 0x00019D9E __TEXT __objc_methname
0x00291A70 0x00012847 __TEXT __cstring
0x002A42B7 0x00001FC1 __TEXT __objc_classname
0x002A6278 0x000046A7 __TEXT __objc_methtype
0x002AA920 0x000061CE __TEXT __ustring
0x002B0AF0 0x00000764 __TEXT __const
0x002B1254 0x000028B8 __TEXT __gcc_except_tab
0x002B3B0C 0x00004EBC __TEXT __unwind_info
0x002B89C8 0x0003662C __TEXT __eh_frame
0x002EF000 0x00000014 __DATA __program_vars
0x002EF014 0x00000284 __DATA __nl_symbol_ptr
0x002EF298 0x0000073C __DATA __la_symbol_ptr
0x002EF9E0 0x000030A4 __DATA __const
0x002F2A84 0x00000590 __DATA __objc_classlist
0x002F3014 0x0000000C __DATA __objc_nlclslist
0x002F3020 0x0000006C __DATA __objc_catlist
0x002F308C 0x000000D8 __DATA __objc_protolist
0x002F3164 0x00000008 __DATA __objc_imageinfo
0x002F3170 0x0002BC80 __DATA __objc_const
0x0031EDF0 0x00003A30 __DATA __objc_selrefs
0x00322820 0x00000014 __DATA __objc_protorefs
0x00322834 0x000006B8 __DATA __objc_classrefs
0x00322EEC 0x00000394 __DATA __objc_superrefs
0x00323280 0x000037C8 __DATA __objc_data
0x00326A48 0x000096D0 __DATA __cfstring
0x00330118 0x00001424 __DATA __objc_ivar
0x00331540 0x00006080 __DATA __data
0x003375C0 0x0000001C __DATA __common
0x003375E0 0x000018E8 __DATA __bss

首列是数据在文件的偏移位置,第二列是这一段占用大小,第三列是段类型,代码段和数据段,第四列是段名称。

每一行的数据都紧跟在上一行后面,如第二行__symbol_stub的地址0x00275FD0就是第一行__text的地址0x00002740加上大小0x00273890,整个可执行文件大致数据分布就是这样。

这里可以清楚看到各种类型的数据在最终可执行文件里占的比例,例如__text表示编译后的程序执行语句,__data表示已初始化的全局变量和局部静态变量,__bss表示未初始化的全局变量和局部静态变量,__cstring表示代码里的字符串常量,等等。

3

接着就是按上表顺序,列出具体的按每个文件列出每个对应字段的位置和占用空间

# Address Size File Name
0x00002740 0x0000003E [ 1] start
0x00002780 0x00000400 [ 2] +[TKPFileInfo parseWithDictionary:]
0x00002B80 0x00000030 [ 2] -[TKPFileInfo fileID]
...

同样首列是数据在文件的偏移地址,第二列是占用大小,第三列是所属文件序号,对应上述Object files列表,最后是名字。

例如第二行代表了文件序号为2(反查上面就是TKPFileInfo.o)的parseWithDictionary方法占用了1000byte大小。

使用

这个文件可以让你了解整个APP编译后的情况,也许从中可以发现一些异常,还可以用这个文件计算静态链接库在项目里占的大小,有时候我们在项目里链了很多第三方库,导致APP体积变大很多,我们想确切知道每个库占用了多大空间,可以给我们优化提供方向。LinkMap里有了每个目标文件每个方法每个数据的占用大小数据,所以只要写个脚本,就可以统计出每个.o最后的大小,属于一个.a静态链接库的.o加起来,就是这个库在APP里占用的空间大小。

分类:技术文章 Tags:
评论

*

*

[…] 《iOS APP可执行文件的组成》 :iOS APP编译后,除了一些资源文件,剩下的就是一个可执行文件,本文分析了该可执行文件的组成。该文章的作者是@bang。 […]

2014年8月13日 9:55

看到了效果,不过不是很清楚该怎么使用。这个怎么能查看第三方库之类文件占用空间呢?这里不都是运行文件时占用的内存吗,第一部分标记使用.a文件的地方也咩用说到占用空间大小哈0.0

2014年8月13日 13:02

这不是运行时占用的空间,是在文件里的占用空间。
每一个目标文件都有一个标号,见第一部分,例如UMANJob.o这个目标文件的编号是280
[280] /Users/bang/…/libMobClickLibrary.a(UMANJob.o)
接着自己写个脚本遍历找第三部分里的每一行,把文件编号为[280]的数据拿出来,把它们的size相加,就是这个目标文件的size。
.a里所有目标文件的size相加就是这个第三方库的占用空间。

[…] 转自:http://blog.cnbang.net/tech/2296/ […]

2014年12月9日 10:20

好东西,学习了

[…] 研究使用了linkmap,linkmap的介绍跟生成可以参考另一篇文章—iOS可执行文件的组成。 […]

[…] 缩减iOS安装包大小是很多中大型APP都要做的事,一般首先会对资源文件下手,压缩图片/音频,去除不必要的资源。这些资源优化做完后,我们还可以尝试对可执行文件进行瘦身,项目越大,可执行文件占用的体积越大,又因为AppStore会对可执行文件加密,导致可执行文件的压缩率低,压缩后可执行文件占整个APP安装包的体积比例大约有80%~90%,还是挺值得优化的。下面介绍一下在研究可执行文件过程中发现的可以优化的点。研究的过程使用了linkmap,linkmap的介绍跟生成可以参考另一篇文章—iOS可执行文件的组成。 […]

[…] 这里用编译属性__attribute__给二进制文件新建一个section,属于__DATA数据段,名字为RCTExport,并在这个段里加入当前方法名。编译器在编译时会找到__attribute__进行处理,为生成的可执行文件加入相应的内容。效果可以从linkmap看出来: […]

2015年4月3日 21:01

[…] 这个宏的作用是用编译属性__attribute__给二进制文件新建一个section,属于__DATA数据段,名字为RCTExport,并在这个段里加入当前方法名。编译器在编译时会找到__attribute__进行处理,为生成的可执行文件加入相应的内容。效果可以从linkmap看出来: […]

2015年4月5日 21:31

[…] 这个宏的作用是用编译属性__attribute__给二进制文件新建一个section,属于__DATA数据段,名字为RCTExport,并在这个 段里加入当前方法名。编译器在编译时会找到__attribute__进行处理,为生成的可执行文件加入相应的内容。效果可以从linkmap看出来: […]

2015年8月24日 8:48

按照这样的方式计算出的.a在可执行文件中占用的大小,与直接查看.a的大小,差别大么?

2015年8月26日 20:30

大,.a大小包括这个库所有可执行代码大小,这里计算出来的是项目里用到的可执行代码大小

[…] 这个宏的作用是用编译属性__attribute__给二进制文件新建一个section,属于__DATA数据段,名字为RCTExport,并在这个段里加入当前方法名。编译器在编译时会找到__attribute__进行处理,为生成的可执行文件加入相应的内容。效果可以从linkmap看出来: […]

[…] 这个宏的作用是用编译属性__attribute__给二进制文件新建一个section,属于__DATA数据段,名字为RCTExport,并在这个段里加入当前方法名。编译器在编译时会找到__attribute__进行处理,为生成的可执行文件加入相应的内容。效果可以从linkmap看出来: […]

[…] 这个宏的作用是用编译属性__attribute__给二进制文件新建一个section,属于__DATA数据段,名字为RCTExport,并在这个段里加入当前方法名。编译器在编译时会找到__attribute__进行处理,为生成的可执行文件加入相应的内容。效果可以从linkmap看出来: […]

2016年3月7日 10:27

[…] 缩减iOS安装包大小是很多中大型APP都要做的事,一般首先会对资源文件下手,压缩图片/音频,去除不必要的资源。这些资源优化做完后,我们还可以尝试对可执行文件进行瘦身,项目越大,可执行文件占用的体积越大,又因为AppStore会对可执行文件加密,导致可执行文件的压缩率低,压缩后可执行文件占整个APP安装包的体积比例大约有80%~90%,还是挺值得优化的。下面介绍一下在研究可执行文件过程中发现的可以优化的点。研究的过程使用了linkmap,linkmap的介绍跟生成可以参考另一篇文章—iOS可执行文件的组成。 […]

[…] 这个宏的作用是用编译属性__attribute__给二进制文件新建一个section,属于__DATA数据段,名字为RCTExport,并在这个段里加入当前方法名。编译器在编译时会找到__attribute__进行处理,为生成的可执行文件加入相应的内容。效果可以从linkmap看出来 […]

[…] 缩减iOS安装包大小是很多中大型APP都要做的事,一般首先会对资源文件下手,压缩图片/音频,去除不必要的资源。这些资源优化做完后,我们还可以尝试对可执行文件进行瘦身,项目越大,可执行文件占用的体积越大,又因为AppStore会对可执行文件加密,导致可执行文件的压缩率低,压缩后可执行文件占整个APP安装包的体积比例大约有80%~90%,还是挺值得优化的。下面介绍一下在研究可执行文件过程中发现的可以优化的点。研究的过程使用了linkmap,linkmap的介绍跟生成可以参考另一篇文章—iOS可执行文件的组成。 […]

[…] 这个宏的作用是用编译属性__attribute__给二进制文件新建一个section,属于__DATA数据段,名字为RCTExport,并在这个段里加入当前方法名。编译器在编译时会找到__attribute__进行处理,为生成的可执行文件加入相应的内容。效果可以从linkmap看出来: […]

[…] 缩减iOS安装包大小是很多中大型APP都要做的事,一般首先会对资源文件下手,压缩图片/音频,去除不必要的资源。这些资源优化做完后,我们还可以尝试对可执行文件进行瘦身,项目越大,可执行文件占用的体积越大,又因为AppStore会对可执行文件加密,导致可执行文件的压缩率低,压缩后可执行文件占整个APP安装包的体积比例大约有80%~90%,还是挺值得优化的。下面介绍一下在研究可执行文件过程中发现的可以优化的点。研究的过程使用了linkmap,linkmap的介绍跟生成可以参考另一篇文章—iOS可执行文件的组成。 […]

[…] 参考文献:http://blog.cnbang.net/tech/2296/ […]

[…] 缩减iOS安装包大小是很多中大型APP都要做的事,一般首先会对资源文件下手,压缩图片/音频,去除不必要的资源。这些资源优化做完后,我们还可以尝试对可执行文件进行瘦身,项目越大,可执行文件占用的体积越大,又因为AppStore会对可执行文件加密,导致可执行文件的压缩率低,压缩后可执行文件占整个APP安装包的体积比例大约有80%~90%,还是挺值得优化的。下面介绍一下在研究可执行文件过程中发现的可以优化的点。研究的过程使用了linkmap,linkmap的介绍跟生成可以参考另一篇文章—iOS可执行文件的组成。编译选项1.编译器优化级别Build Settings->Optimization Level有几个编译优化选项,release版应该选择Fastest, Smalllest,这个选项会开启那些不增加代码大小的全部优化,并让可执行文件尽可能小。2.去除符号信息Strip Debug Symbols During Copy 和 Symbols Hidden by Default 在release版本应该设为yes,可以去除不必要的调试符号。Symbols Hidden by Default会把所有符号都定义成”private extern”,具体意思和作用我还不清楚,有待研究,但设了后会减小体积。这些选项目前都是XCode默认选项,但旧版XCode生成的项目可能不是,可以检查一下。其他优化还可以参考苹果的官方文档—CodeFootprint.pdf第三方库统计项目里会引入很多第三方静态库,如果能知道这些第三方库在可执行文件里占用的大小,就可以评估是否值得去找替代方案去掉这个第三方库。我们可以从linkmap中统计出这个信息,我写了个node.js脚本,可以通过linkmap统计每个.o目标文件占用的体积和每个.a静态库占用的体积,并进行排序。详见这里(需翻墙)。ARC->MRC有人提出用ARC写的代码编译出来的可执行文件是会比用MRC大的,原因大致是ARC代码会在某些情况多出一些retain和release的指令,例如调用一个方法,它返回的对象会被retain,退出作用域后会被release,MRC就不需要,汇编指令变多,机器码变多,可执行文件就变大了。还有其他细节实现的区别,先不纠结了。那用ARC究竟会增大多少体积?我觉得从汇编指令的增多减少去算是很难算准确的,这东西涉及细节太多,还是得从统计的角度计算。做了几个对比试验,统计了几个同时支持ARC/MRC的开源项目在开启/关闭ARC的情况下__TEXT代码段的大小对比。只对比__TEXT代码段是因为:ARC对可执行文件大小的影响几乎都是在代码段可执行文件会进行某种对齐,例如有些段在不足32K的时候填充0直到对齐32K,若用可执行文件大小对比结果可能是对齐后的,不准确。实验数据: […]