iOS標記指針(Tagged Pointer)技術

在2013年9月,蘋果推出了iPhone5s,配備了首個採用64位架構的A7雙核處理器,爲了節省內存和提升執行效率,蘋果提出了標記指針(Tagged Pointer) 的概念。對於64位程序,引入Tagged Pointer後,相關邏輯能減小一半的內存佔用,以及3倍的訪問速度提高,100倍的建立、銷燬速度提高。html

原有的對象爲何會浪費內存?

假設咱們要存儲一個NSNumber對象,其值是一個整數。正常狀況下,若是這個整數只是一個NSInteger的普通變量,那麼它所佔用的內存是與CPU的位數有關,在32位CPU下佔4個字節,在64位CPU下是佔8個字節的。而指針類型的大小一般也是與CPU位數相關,一個指針所佔用的內存在32位CPU下爲4個字節,在64位CPU下也是8個字節。objective-c

因此一個普通的iOS程序,若是沒有Tagged Pointer對象,從32位機器遷移到64位機器中後,雖然邏輯沒有任何變化,但這種NSNumber、NSDate一類的對象所佔用的內存會翻倍。bash

對象爲何會浪費內存

效率上的問題,爲了存儲和訪問一個NSNumber對象,須要在堆上爲其分配內存,另外還要維護它的引用計數,管理它的生命期。這些都給程序增長了額外的邏輯,形成運行效率上的損失。架構

Tagged Pointer優化

爲了改進上面提到的內存佔用和效率問題,蘋果提出了Tagged Pointer對象。因爲NSNumber、NSDate一類的變量自己的值須要佔用的內存大小經常不須要8個字節,拿整數來講,4個字節所能表示的有符號整數就能夠達到20多億(注:2^31=2147483648,另外1位做爲符號位),對於絕大多數狀況都是能夠處理的。spa

使用標記指針.png

NSNumber的優化

Tagged Pointer一個比較典型的應用就是NSNumber,在64位環境下,對於通常的數字,NSNumber不用再分配內存了。咱們看看NSNumber是如何運用Tagged Pointer的:指針

代碼 objc:

NSNumber *number1=@1;
NSNumber *number2=@2;
NSNumber *number3=@3;

NSLog(@"number1 pointer is %p", number1);
NSLog(@"number2 pointer is %p", number2);
NSLog(@"number3 pointer is %p", number3);

在64位模擬器中運行後,我獲得了以下結果:

number1 pointer is 0xb000000000000012
number2 pointer is 0xb000000000000022
number3 pointer is 0xb000000000000032
複製代碼

能夠看出number一、number2和number3的值前4位都是0xb,後4位都是0x2(指針的Tag),中間就是實際的取值,所以,這些NSNumber已經不須要再分配內存(指堆中內存)了,直接能夠把實際的值保存到指針中,而無需再去訪問堆中的數據。這無疑提升的內存訪問速度和總體運算速度。也就是說Tagged Pointer自己就能夠表示一個NSNumber了。code

那麼若是一個數超過了Tagged Pointer所能表示的範圍,系統會怎麼處理?看看這段代碼:視頻

代碼 objc:
NSNumber*numberBig=@(0x2233567890ABCDEF);
NSLog(@"numberBig pointer is %p", numberBig);

在64位模擬器中運行後,我獲得了以下結果:
代碼 objc:
numberBig pointer is 0x1394026a0
複製代碼

numberBig指針最後4位都是0,應該是分配在堆中的對象。所以,若是NSNumber超出了Tagged Pointer所能表示的範圍,系統會自動採用分配成對象,能夠根據指針的最後4位是否爲0來區分。htm

isa指針優化

查看NSObject類的頭文件,你會發現這段定義:

@interface NSObject: <NSObject>
 { 
  Class isa;
 }
複製代碼

全部類都繼承自NSObject,所以每一個對象都有一個isa指針指向它所屬的類。在《ARM64 and You》文章中指出:在32位環境下,對象的引用計數都保存在一個外部的表中,而對引用計數的增減操做都要先鎖定這個表,操做完成後才解鎖。這個效率是很是慢的。而在64位環境下,isa也是64位,實際做爲指針部分只用到的其中33位,剩餘的部分會運用到Tagged Pointer的概念,其中19位將保存對象的引用計數,這樣對引用計數的操做只須要原子的修改這個指針便可,若是引用計數超出19位,纔會將引用計數保存到外部表,而這種狀況每每是不多的,所以效率將會大大提升。

在WWDC2013的《Session 404 Advanced in Objective-C》視頻中,看到蘋果對於Tagged Pointer特色的介紹:

1.Tagged Pointer專門用來存儲小的對象,例如NSNumber和NSDate
 2.Tagged Pointer指針的值再也不是地址了,而是真正的值。
     實際上它再也不是一個對象了,它只是一個披着對象皮的普通變量而已。
     它的內存並不存儲在堆中,也不須要malloc和free。
 3.在內存讀取上有着3倍的效率,建立時比之前快106倍。
複製代碼

蘋果引入Tagged Pointer,不但減小了64位機器下程序的內存佔用,還提升了運行效率。完美地解決了小內存對象在存儲和訪問效率上的問題。

Tagged Pointer的引入也帶來了問題,即Tagged Pointer 由於並非真正的對象,而是一個僞對象,因此你若是徹底把它當成對象來使,可能會讓它露馬腳。全部對象都有 isa指針,而Tagged Pointer實際上是沒有的,由於它不是真正的對象。 由於不是真正的對象,因此若是你直接訪問Tagged Pointer的isa成員的話,在編譯時將會有警告。

總之:

蘋果將Tagged Pointer引入,給64位系統帶來了內存的節省和運行效率的提升。Tagged Pointer經過在其最後一個bit位設置一個特殊標記,用於將數據直接保存在指針自己中。Tagged Pointer並非真正的對象,使用時須要注意不要直接訪問其isa變量。

參考文章:  Objective-C對象模型及應用 ARM64 and You

相關文章
相關標籤/搜索