性能优化(一)---内存管理

373 阅读6分钟

一、内存布局

内核区:(例:4bg内存只有3gb用来储存信息,1bg给内核区↓地址为0xc0000000←3bg↓)高
栈区
----------------------------

方法、int等临时变量(栈帧逐渐从上往下往堆的方向扩展)
堆区的增长则是自下而上,若碰面即为堆栈(内存)溢出
----------------------------
堆区
未初始化数据(.bss)段:全局静态变量--也称为静态区
已初始化数据(.data)段--也称为常量区
代码段(.text):二进制程序存放
保留段:给系统提供必要的保留空间(↑0x00400000↑)低
  • 程序加载到内存会走向未初始化数据(.bss)段、已初始化数据(.data)段代码段(.text)
    // 0x1 常量 静态
    int a = 10;
    NSLog(@"%p",&a); // 栈 -- 0x7 一般情况都是 栈
    NSObject *obj = [NSObject new]; // 对象 --
    NSLog(@"%@ - %p",obj,&obj); // 0x6 堆   -   指针的变量存在栈区
    NSArray *array = [[NSArray alloc] init];
    NSLog(@"%@-%p",array,&array);

// 若想找到一个对象,先在栈区找到其指针,在根据指针所指的堆区中的位置读取其值

// 尽量采用封装方法 :随着方法中的变量增多,会更加难以读取,所以要增加嵌套,以空间换时间

// 疑问:重写description、方法和函数的区别

二、内存管理方案

1、TaggedPointer:小对象-NSNumber、NSDate

  • NSNumber 有 2^31 位

    1字节 = 8位

    int : 4字节*8 = 32

    导致一般用NSNumber会大量浪费内存(因为一般存入的数字远小于NSNumber的大小)

  • ~ 按位取反 : 100001 ~ 011110

  • 10000111 <<3 左移3位 10000111000

  • 10000111 >>3 右移3位 10000

  • ^ 异或操作:两个相同为0,不同为1 , 常用于翻转交换

  • 交换:

    a = 1010 0001

    b = 0000 1100

    a = a^b // 1010 1101

    b = a^b // 1010 0001

    a = a^b // 0000 1100

  • 源码:

    static inline void * _Nonnull
    _objc_makeTaggedPointer(objc_tag_index_t tag, uintptr_t value)
    {
        // PAYLOAD_LSHIFT and PAYLOAD_RSHIFT are the payload extraction shifts.
        // They are reversed here for payload insertion.
    
        // assert(_objc_taggedPointersEnabled());
        if (tag <= OBJC_TAG_Last60BitPayload) 
        {
            // assert(((value << _OBJC_TAG_PAYLOAD_RSHIFT) >> _OBJC_TAG_PAYLOAD_LSHIFT) == value);
            uintptr_t result =
                (_OBJC_TAG_MASK | 
                 ((uintptr_t)tag << _OBJC_TAG_INDEX_SHIFT) | 
                 ((value << _OBJC_TAG_PAYLOAD_RSHIFT) >> _OBJC_TAG_PAYLOAD_LSHIFT));
            return _objc_encodeTaggedPointer(result);
        }
      	else
        {
            // assert(tag >= OBJC_TAG_First52BitPayload);
            // assert(tag <= OBJC_TAG_Last52BitPayload);
            // assert(((value << _OBJC_TAG_EXT_PAYLOAD_RSHIFT) >> _OBJC_TAG_EXT_PAYLOAD_LSHIFT) == value);
            uintptr_t result =
                (_OBJC_TAG_EXT_MASK |
                 ((uintptr_t)(tag - OBJC_TAG_First52BitPayload) << _OBJC_TAG_EXT_INDEX_SHIFT) |
                 ((value << _OBJC_TAG_EXT_PAYLOAD_RSHIFT) >> _OBJC_TAG_EXT_PAYLOAD_LSHIFT));
            return _objc_encodeTaggedPointer(result);
        }
    }
    
    static inline void * _Nonnull
    _objc_encodeTaggedPointer(uintptr_t ptr)
    {
        return (void *)(objc_debug_taggedpointer_obfuscator ^ ptr);
      	// 做标记,表明为taggedpointer,不是真正的对象,而是个值
      	// 告诉系统这是个小对象,不需要太麻烦的处理
      
    }
    
    static inline uintptr_t
    _objc_decodeTaggedPointer(const void * _Nullable ptr)
    {
        return (uintptr_t)ptr ^ objc_debug_taggedpointer_obfuscator;
    }
    
    
    static void
    initializeTaggedPointerObfuscator(void)
    {
      	// 老系统不做编码
        if (sdkIsOlderThan(10_14, 12_0, 12_0, 5_0, 3_0) ||
            // Set the obfuscator to zero for apps linked against older SDKs,
            // in case they're relying on the tagged pointer representation.
            DisableTaggedPointerObfuscation) {
            objc_debug_taggedpointer_obfuscator = 0;
        } else {
            // Pull random data into the variable, then shift away all non-payload bits.
            arc4random_buf(&objc_debug_taggedpointer_obfuscator,
                           sizeof(objc_debug_taggedpointer_obfuscator));
            objc_debug_taggedpointer_obfuscator &= ~_OBJC_TAG_MASK;
        }
    }
    
    • 手动将系统编码的NSNumber编码的对象解码后会发现,解码后对象地址最后一位为类型

      int :2

      float :4

      long :3

      double:5

    • 紧挨末尾的倒数位为NSNumber的值

    • 说明小对象类型的地址不止存储地址空间,还会存储其类型

      也就不需要去栈里找指针,再去堆里找值,也不用64位存储

      (牛逼哇)

    • NSString里中文为CFString,因为中文是特殊符号,如果是abcd这种的就会是TaggedPointerString类型,但是长度太长的话也会变成CFString,因为已经不是小对象

    • TaggedPointerString在每次赋值(setter)的时候不需要retain和release,但是CFString则需要一直retain和release,如果处于多线程中,就有可能崩溃

    • 在内存读取上有3倍的效率,创建时比以前快106倍

2、NONPOINTER_ISA:非指针类型isa

7 |0|0|0|0|0|0|0|0| 0

  • 联合体union

        union {
            char bits; // 0000 0000
            // 位域(顺延)
            struct {
                char front : 1;
                char back :  1;
                char left :  1;
                char right : 1;
            };
            
        } _direction;
    

    从右往左:

  • 第一位 nonpointer :

    表示是否对isa指针开启指针优化

    0:纯isa指针 1:不止是类对象地址,isa中包含了类信息、对象的引用计数等

  • 第二位 has_assoc :

    关联对象标志位

    0没有,1存在

  • 第三位 has_cxx_dtor :

    该对象是否有C++或者Objec的析构器,如果有析构函数,则需要做析构逻辑,如果没有,则可以更快的释放对象

  • shiftcls :

存储类指针的值。开启指针优化的情况下,在arm64位中有33位用来存储类指针

疑问:什么是类指针,为什么要这么多

  • magic :

用于调试器判断当前对象是真的对象还是没有初始化的空间

  • weakly_referenced :

标志对象是否被只想或者曾经指向一个ARC的弱变量,没有弱引用的对象可以更快释放

  • deallocating :

标志对象是否正在释放内存

  • has_sidetable_rc :

当对象引用计数大于10时,则需要借用该变量存储进位

  • 接下来19位 extra_rc :

    当表示该对象的引用计数值,实际上是引用计数之减1

    例如,如果对象的引用计数为10,那么extra_rc 为 9 。如果引用计数大于10,则需要使用到下面的has_sidetable_rc

3、散列表:引用计数,弱引用表

table = &SideTables()[obj];

static StripedMap<SideTable>& SideTables() {
    return *reinterpret_cast<StripedMap<SideTable>*>(SideTableBuf);
}
  • SideTables() 中包含了很多张SideTable

  • SideTable中含有

    • Spinlock_t :自旋锁
    • RefcountMap:引用计数表
    • weak_table_t : 弱引用表
    struct SideTable {
        spinlock_t slock;
        RefcountMap refcnts;
        weak_table_t weak_table;
    
        SideTable() {
            memset(&weak_table, 0, sizeof(weak_table));
        }
    
        ~SideTable() {
            _objc_fatal("Do not delete SideTable.");
        }
    
        void lock() { slock.lock(); }
        void unlock() { slock.unlock(); }
        void forceReset() { slock.forceReset(); }
    
        // Address-ordered lock discipline for a pair of side tables.
    
        template<HaveOld, HaveNew>
        static void lockTwo(SideTable *lock1, SideTable *lock2);
        template<HaveOld, HaveNew>
        static void unlockTwo(SideTable *lock1, SideTable *lock2);
    };
    
  • 为什么是多张sideTable,而不是一张表?

    在同一张表中,若修改一个属性则另一个属性需要等待,因为表的自旋锁在忙等

  • StripedMap

    static StripedMap<SideTable>& SideTables() {
        return *reinterpret_cast<StripedMap<SideTable>*>(SideTableBuf);
    }
    
    template<typename T>
    class StripedMap {
    #if TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR
        enum { StripeCount = 8 };
    #else
        enum { StripeCount = 64 };
    #endif
    
        struct PaddedT {
            T value alignas(CacheLineSize);
        };
    
        PaddedT array[StripeCount];
    
        static unsigned int indexForPointer(const void *p) {
          	// reinterpret_cast 十六进制转十进制
            uintptr_t addr = reinterpret_cast<uintptr_t>(p);
            return ((addr >> 4) ^ (addr >> 9)) % StripeCount;
          	// 防止散列冲突/数组越界的方法
        }
    
     public:
      	// 重写了操作符  (啥是重装操作符)
        T& operator[] (const void *p) { 
          	// 获取散列表
            return array[indexForPointer(p)].value; 
          	// 外部传入的对象是p
        }
        const T& operator[] (const void *p) const { 
            return const_cast<StripedMap<T>>(this)[p]; 
        }
    
        // Shortcuts for StripedMaps of locks.
        void lockAll() {
            for (unsigned int i = 0; i < StripeCount; i++) {
                array[i].value.lock();
            }
        }
    
        void unlockAll() {
            for (unsigned int i = 0; i < StripeCount; i++) {
                array[i].value.unlock();
            }
        }
    
        void forceResetAll() {
            for (unsigned int i = 0; i < StripeCount; i++) {
                array[i].value.forceReset();
            }
        }
    
        void defineLockOrder() {
            for (unsigned int i = 1; i < StripeCount; i++) {
                lockdebug_lock_precedes_lock(&array[i-1].value, &array[i].value);
            }
        }
    
        void precedeLock(const void *newlock) {
            // assumes defineLockOrder is also called
            lockdebug_lock_precedes_lock(&array[StripeCount-1].value, newlock);
        }
    
        void succeedLock(const void *oldlock) {
            // assumes defineLockOrder is also called
            lockdebug_lock_precedes_lock(oldlock, &array[0].value);
        }
    
        const void *getLock(int i) {
            if (i < StripeCount) return &array[i].value;
            else return nil;
        }
        
    #if DEBUG
        StripedMap() {
            // Verify alignment expectations.
            uintptr_t base = (uintptr_t)&array[0].value;
            uintptr_t delta = (uintptr_t)&array[1].value - base;
            assert(delta % CacheLineSize == 0);
            assert(base % CacheLineSize == 0);
        }
    #else
        constexpr StripedMap() {}
    #endif
    };
    
  • RefcountMap

    // RefcountMap disguises its pointers because we 
    // don't want the table to act as a root for `leaks`.
    typedef objc::DenseMap<DisguisedPtr<objc_object>,size_t,true> RefcountMap;
    // 就是个存key和value的map,不是个table 
    

三、ARC&MRC

四、引用计数

五、弱引用

六、自动释放池