64位与Tagged Pointer
yecat_10
11年前
<p>在Mac OS X 10.6(Snow Leopard)中开始支持64位,如今最新版本iPhone 5s也开始采用了Arm64架构。在64位化的过程中,其中一个比较关键的改进就是,Mac OS 10.7(Lion)和iOS 7的64位环境先后引入了Tagged Pointer。下面就简单地来介绍一下Tagged Pointer,在介绍Tagged Pointer之前有必要介绍一下指针地址对齐概念和64位环境的一些变化。</p> <h2>指针地址对齐</h2> <p>在32位环境下,如果要读取一个32位整数,如果这个32位整数在内存地址为0x00000002-0x00000006(仅作举例,这个地址一般是被系统保留的)的内存上,读取这个整数会消耗2个CPU周期,而如果这个数在0x00000004-0x00000008的内存上只需要一个CPU周期。为了加快内存的CPU访问,程序都使用了指针地址对齐概念。指针地址对齐就是指在分配堆中的内存时往往采用偶数倍或以2为指数倍的内存地址作为地址边界。几乎所有系统架构,包括Mac OS和iOS,都使用了地址对齐概念。</p> <pre> void *a = malloc(1); void *b = malloc(3); NSLog(@"a: %p",a); NSLog(@"b: %p",b); </pre> <p>运行这段代码后,我得到了如下结果:</p> <pre> a: 0x8c11e20 b: 0x8c11e30 </pre> <p>可以看到,a和b指针的最后4位都是0,虽然a只占用1个字节,但是a和b的地址却相差16个字节。因为iOS中是以16个字节为内存分配边界的,或者说iOS的指针地址对齐是以16个字节为对齐边界的。进一步说,iOS中分配的内存地址最后4位永远都是0。</p> <h2>64位地址</h2> <p>在不久前发布iPhone5s中采用了Arm64的CPU,同时也支持了64位的App。64位App中指针大小也扩大到64位,就是理论上可以支持最大2 <sup>64</sup> 字节(达千万T字节)的内存地址空间。而对于大多数应用来说,这么大的地址空间完全是浪费的。也就是说64位环境下,内存地址的前面很多位一般都是0。</p> <h2>Tagged Pointer</h2> <p>由于指针地址对齐概念和64位超大地址的出现,指针地址仅仅作为内存的地址是比较浪费的,我们可以在指针地址中保存或附加更多的信息。这就引入了Tagged Pointer概念。Tagged Pointer是指那些指针中包含特殊属性或信息的指针。其中指针对齐概念可以让我们来标识一个指针是否是Tagged Pointer以及相关类型,64位的地址指针又为我们提供保存额外信息的足够空间。如今,iOS 7的64位环境和Mac OS 10.7(Lion)中开始引入了Tagged Pointer。</p> <h2>NSNumber的优化</h2> <p>Tagged Pointer一个比较典型的应用就是NSNumber,在64位环境下,对于一般的数字,NSNumber不用再分配内存了。我们看看NSNumber是如何运用Tagged Pointer的:</p> <pre> NSNumber *number3 = @3; NSNumber *number4 = @4; NSNumber *number9 = @9; NSLog(@"number3 pointer is %p", number3); NSLog(@"number4 pointer is %p", number4); NSLog(@"number9 pointer is %p", number9); </pre> <p>在64位模拟器中运行后,我得到了如下结果:</p> <pre> number3 pointer is 0xb000000000000032 number4 pointer is 0xb000000000000042 number9 pointer is 0xb000000000000092 </pre> <p>可以看出 <code>number3 、 <code>number4 和 <code>number9 的值前4位都是0xb,后4位都是0x2(指针的Tag),中间就是实际的取值,因此,这些NSNumber已经不需要再分配内存(指堆中内存)了,直接可以把实际的值保存到指针中,而无需再去访问堆中的数据。这无疑提高的内存访问速度和整体运算速度。 </code></code></code></p> <p><code><code><code>也就是说Tagged Pointer本身就可以表示一个NSNumber了,在64位环境下运行这段代码:</code></code></code></p> <pre> <code><code><code>NSLog(@"0xb000000000000052's class is %@",[(NSNumber*)0xb000000000000052 class]); </code></code></code></pre> <pre> <code><code><code>0xb000000000000052's class is __NSCFNumber </code></code></code></pre> <p><code><code><code>那么如果一个数超过了Tagged Pointer所能表示的范围,系统会怎么处理?看看这段代码:</code></code></code></p> <pre> <code><code><code>NSNumber *numberBig = @(0x1234567890ABCDEF); NSLog(@"numberBig pointer is %p", numberBig); </code></code></code></pre> <p><code><code><code>在64位模拟器中运行后,我得到了如下结果:</code></code></code></p> <pre> <code><code><code>numberBig pointer is 0x1094026a0 </code></code></code></pre> <p><code><code><code>可以看出 <code>numberBig 指针最后4位都是0,应该是分配在堆中的对象。因此,如果NSNumber超出了Tagged Pointer所能表示的范围,系统会自动采用分配成对象,可以根据指针的最后4位是否为0来区分。 </code></code></code></code></p> <h2><code><code><code><code>isa指针优化</code></code></code></code></h2> <p><code><code><code><code>查看NSObject类的头文件,你会发现这段定义:</code></code></code></code></p> <pre> <code><code><code><code>@interface NSObject <NSObject> { Class isa; } </code></code></code></code></pre> <p><code><code><code><code>所有类都继承自NSObject,因此每个对象都有一个isa <sup> <a href="/misc/goto?guid=4959673301684326940" rel="nofollow">1</a> </sup> 指针指向它所属的类。在《 <a href="/misc/goto?guid=4959673301771629589" rel="nofollow">ARM64 and You</a> 》文章中指出: </code></code></code></code></p> <p><code><code><code><code>在32位环境下,对象的引用计数都保存在一个外部的表中,而对引用计数的增减操作都要先锁定这个表,操作完成后才解锁。这个效率是非常慢的。</code></code></code></code></p> <p><code><code><code><code>而在64位环境下,isa也是64位,实际作为指针部分只用到的其中33位,剩余的部分会运用到Tagged Pointer的概念,其中19位将保存对象的引用计数,这样对引用计数的操作只需要原子的修改这个指针即可,如果引用计数超出19位,才会将引用计数保存到外部表,而这种情况往往是很少的,因此效率将会大大提高。</code></code></code></code></p> <h2><code><code><code><code>参考文献</code></code></code></code></h2> <ul> <li><code><code><code><code>Objective-C对象模型及应用: <a href="/misc/goto?guid=4959673301860261190" rel="nofollow">http://blog.devtang.com/blog/2013/10/15/objective-c-object-model/</a> </code></code></code></code></li> <li><code><code><code><code>ARM64 and You: <a href="/misc/goto?guid=4959673301771629589" rel="nofollow">http://www.mikeash.com/pyblog/friday-qa-2013-09-27-arm64-and-you.html</a> </code></code></code></code></li> </ul> <p> </p> <p><code><code><code>来自: <a href="/misc/goto?guid=4959673301954461831" rel="nofollow">http://blog.xcodev.com/blog/2013/10/21/tagged-pointer-and-64-bit/</a></code></code></code></p> <p> </p>