内存管理 之 内存布局及Tagged Pointer

1、内存布局

在前面,我们也提到过应用程序中的内存地址分配,主要分为代码段、数据段、堆、栈、内核区,如下图所示:
内存管理 之 内存布局及Tagged Pointer_第1张图片
屏幕快照 2019-01-07 下午6.08.52.png
//数据段: 全局变量(已初始化)
int a = 10;

//数据段: 全局变量(未初始化)
int b;

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    //数据段: 静态变量(已初始化)
    static int c = 20;
    
    //数据段: 静态变量(未初始化)
    static int d;
    
    //栈:局部变量(未初始化)
    int e;
    
    //栈: 局部变量(已初始化)
    int f = 20;
    
    //数据段: 字符串常量
    NSString *str = @"123";
    
    //堆
    NSObject *obj = [[NSObject alloc] init];
}
@end
2、Tagged Pointer
NSNumber *number1 = @(10);
NSLog(@"number1---> %p",number1);
   
NSNumber *number2 = @(100000000);
NSLog(@"number2---> %p",number2);
    
NSNumber *number3 = @(100000000000000000);//18位
NSLog(@"number3---> %p",number3);

打印结果:
===========================================
number1---> 0xf244e62484888f3b
number2---> 0xf244e624dbd69f9b
number3---> 0x6000026d1b40

看上面的例子,你会不会感到疑惑,他们都是NSNumber类型的,但是为什么他们的地址格式不一样呢?
答:这是因为从64bit开始,iOS引入了Tagged Pointer技术,用于优化NSNumber、NSDate、NSString等小对象的存储。
只有当指针不够存储数据时,才会使用原先动态分配内存的方式来存储数据。
objc_msgSend能识别Tagged Pointer,比如NSNumber的intValue方法,直接从指针提取数据,节省了以前的调用开销。

在没有使用Tagged Pointer之前, NSNumber等对象需要动态分配内存、维护引用计数等,NSNumber指针存储的是堆中NSNumber对象的地址值。
内存管理 之 内存布局及Tagged Pointer_第2张图片
屏幕快照 2019-01-07 下午6.11.02.png

使用Tagged Pointer之后,NSNumber指针里面存储的数据变成了:Tag + Data,也就是将数据直接存储在了指针中。
内存管理 之 内存布局及Tagged Pointer_第3张图片
屏幕快照 2019-01-07 下午6.11.16.png
3、扩展
  • <1>下面这两段代码执行的效果怎么样?
//代码段1:
 dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
    for (int i = 0; i<1000; i++) {
        dispatch_async(queue, ^{
            self.name = [NSString stringWithFormat:@"abcdefjhijk"];
        });
    }
//代码段2:
 dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
    for (int i = 0; i<1000; i++) {
        dispatch_async(queue, ^{
            self.name = [NSString stringWithFormat:@"abc"];
        });
    }

答:代码段1会发生崩溃,代码段2不会崩溃。
因为代码段1崩溃的原因是因为多线程的安全隐患。多条线程同时读写同一资源变量时,导致每个线程都会在name的setName方法里进行release等操作,name会被释放多次,造成坏内存访问。
那为什么代码段2不会崩溃呢?因为较短的字符串“abc”是用Tagged Pointer来存储的。它是直接将地址值赋给name的,简洁来说,它并不是一个OC对象,没有OC对象的set、get方法,也没有release,所以不会崩溃。

  • <2>如何判断一个指针是否为Tagged Pointer?
    根据底层源码可以得知:
    iOS平台,最高有效位是1(第64bit)
    Mac平台,最低有效位是1
    这两种情况下,指针为Tagged Pointer。

你可能感兴趣的:(内存管理 之 内存布局及Tagged Pointer)