美文网首页
内存管理 之 内存布局及Tagged Pointer

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

作者: ychen3022 | 来源:发表于2019-01-07 18:08 被阅读6次
    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对象的地址值。 屏幕快照 2019-01-07 下午6.11.02.png 使用Tagged Pointer之后,NSNumber指针里面存储的数据变成了:Tag + Data,也就是将数据直接存储在了指针中。 屏幕快照 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

          本文链接:https://www.haomeiwen.com/subject/rmlurqtx.html