美文网首页
六、cache_t分析

六、cache_t分析

作者: Mjs | 来源:发表于2020-09-18 15:17 被阅读0次

    class结构

    struct objc_class : objc_object {
        // Class ISA;
        Class superclass;
        cache_t cache;             // formerly cache pointer and vtable
        class_data_bits_t bits;    // class_rw_t * plus custom rr/alloc flags
    
        class_rw_t *data() const {
            return bits.data();
        }
    

    之前我们已经看了data了放了什么,现在我们来看看cache_t

    cache_t

    补充

    系统架构

    macOS i386
    模拟器: x86
    真机:arm64

    #if defined(__arm64__) && __LP64__
    #define CACHE_MASK_STORAGE CACHE_MASK_STORAGE_HIGH_16
    #elif defined(__arm64__) && !__LP64__
    #define CACHE_MASK_STORAGE CACHE_MASK_STORAGE_LOW_4
    #else
    #define CACHE_MASK_STORAGE CACHE_MASK_STORAGE_OUTLINED
    #endif
    
    源码
    
    struct cache_t {
    #if CACHE_MASK_STORAGE == CACHE_MASK_STORAGE_OUTLINED
        explicit_atomic<struct bucket_t *> _buckets;
        explicit_atomic<mask_t> _mask;
    #elif CACHE_MASK_STORAGE == CACHE_MASK_STORAGE_HIGH_16
        explicit_atomic<uintptr_t> _maskAndBuckets;
        mask_t _mask_unused;
        
        // How much the mask is shifted by.
        static constexpr uintptr_t maskShift = 48;
        
        // Additional bits after the mask which must be zero. msgSend
        // takes advantage of these additional bits to construct the value
        // `mask << 4` from `_maskAndBuckets` in a single instruction.
        static constexpr uintptr_t maskZeroBits = 4;
        
        // The largest mask value we can store.
        static constexpr uintptr_t maxMask = ((uintptr_t)1 << (64 - maskShift)) - 1;
        
        // The mask applied to `_maskAndBuckets` to retrieve the buckets pointer.
        static constexpr uintptr_t bucketsMask = ((uintptr_t)1 << (maskShift - maskZeroBits)) - 1;
        
        // Ensure we have enough bits for the buckets pointer.
        static_assert(bucketsMask >= MACH_VM_MAX_ADDRESS, "Bucket field doesn't have enough bits for arbitrary pointers.");
    #elif CACHE_MASK_STORAGE == CACHE_MASK_STORAGE_LOW_4
    ...
    #else
    #error Unknown cache mask storage type.
    #endif
    #if __LP64__
        uint16_t _flags;
    #endif
        uint16_t _occupied;
    
    public:
        static bucket_t *emptyBuckets();
        
        struct bucket_t *buckets();
        mask_t mask();
        mask_t occupied();
        void incrementOccupied();
        void setBucketsAndMask(struct bucket_t *newBuckets, mask_t newMask);
        void initializeToEmpty();
    
        unsigned capacity();
        bool isConstantEmptyCache();
        bool canBeFreed();
    
    

    explicit_atomic 是为了安全操作,暂不细看

    struct bucket_t {
    private:
        // IMP-first is better for arm64e ptrauth and no worse for arm64.
        // SEL-first is better for armv7* and i386 and x86_64.
    #if __arm64__
        explicit_atomic<uintptr_t> _imp;
        explicit_atomic<SEL> _sel;
    #else
        explicit_atomic<SEL> _sel;
        explicit_atomic<uintptr_t> _imp;
    #endif
    

    bucket_t存着imp和sel

    详看CACHE_MASK_STORAGE_HIGH_16 真机进行了什么操作


    cache.png

    测试代码:

            LGPerson *p  = [LGPerson alloc];
            Class pClass = [LGPerson class];
    //        p.lgName     = @"Cooci";
    //        p.nickName   = @"KC";
            // 缓存一次方法 sayHello
            // 4
            [p sayHello];
    

    通过lldb打印

    (lldb) p/x pClass
    (Class) $0 = 0x0000000100002288 LGPerson
    (lldb) p (cache_t*)0x0000000100002298
    (cache_t *) $1 = 0x0000000100002298
    (lldb) p *$1
    (cache_t) $2 = {
      _buckets = {
        std::__1::atomic<bucket_t *> = 0x000000010101fe00 {
          _sel = {
            std::__1::atomic<objc_selector *> = ""
          }
          _imp = {
            std::__1::atomic<unsigned long> = 11912
          }
        }
      }
      _mask = {
        std::__1::atomic<unsigned int> = 3
      }
      _flags = 32804
      _occupied = 1
    }
    (lldb) p $2.buckets()
    (bucket_t *) $3 = 0x000000010101fe00
    (lldb) p *$3
    (bucket_t) $4 = {
      _sel = {
        std::__1::atomic<objc_selector *> = ""
      }
      _imp = {
        std::__1::atomic<unsigned long> = 11912
      }
    }
    (lldb) p $4.sel()
    (SEL) $5 = "sayHello"
    (lldb) 
    

    或者我们在没有源码的环境中添加结构体,进行强转测试

    
    typedef uint32_t mask_t;  // x86_64 & arm64 asm are less efficient with 16-bits
    
    struct lg_bucket_t {
        SEL _sel;
        IMP _imp;
    };
    
    struct lg_cache_t {
        struct lg_bucket_t * _buckets;
        mask_t _mask;
        uint16_t _flags;
        uint16_t _occupied;
    };
    
    struct lg_class_data_bits_t {
        uintptr_t bits;
    };
    
    struct lg_objc_class {
        Class ISA;
        Class superclass;
        struct lg_cache_t cache;             // formerly cache pointer and vtable
        struct lg_class_data_bits_t bits;    // class_rw_t * plus custom rr/alloc flags
    };
    
    int main(int argc, const char * argv[]) {
        @autoreleasepool {
            LGPerson *p  = [LGPerson alloc];
            Class pClass = [LGPerson class];  // objc_clas
            [p say1];
            [p say2];
            [p say3];
            [p say4];
             
            
            struct lg_objc_class *lg_pClass = (__bridge struct lg_objc_class *)(pClass);
            NSLog(@"%hu - %u",lg_pClass->cache._occupied,lg_pClass->cache._mask);
            for (mask_t i = 0; i<lg_pClass->cache._mask; i++) {
                // 打印获取的 bucket
                struct lg_bucket_t bucket = lg_pClass->cache._buckets[i];
                NSLog(@"%@ - %p",NSStringFromSelector(bucket._sel),bucket._imp);
            }
        }
        return 0;
    }
    

    打印结果

    LGPerson say : -[LGPerson say1]
    LGPerson say : -[LGPerson say2]
    LGPerson say : -[LGPerson say3]
     LGPerson say : -[LGPerson say4]
     2 - 7
     say4 - 0x29b8
     (null) - 0x0
     say3 - 0x29e8
     (null) - 0x0
     (null) - 0x0
     (null) - 0x0
     (null) - 0x0
    
    疑问

    为什么_occupied,_mask是什么,为什么是2-7?
    我们查看源码,只有方法中才会对数据进行操作,我们在源码中看到incrementOccupied()

    {
       _occupied++;
    }
    

    全局搜索,在cache_t::insert(Class cls, SEL sel, IMP imp, id receiver)找到使用这个方法的地方

    void cache_t::insert(Class cls, SEL sel, IMP imp, id receiver)
    {
    ....
        // Use the cache as-is if it is less than 3/4 full
        mask_t newOccupied = occupied() + 1;
        unsigned oldCapacity = capacity(), capacity = oldCapacity;
        if (slowpath(isConstantEmptyCache())) {
            // Cache is read-only. Replace it.
            if (!capacity) capacity = INIT_CACHE_SIZE;
            reallocate(oldCapacity, capacity, /* freeOld */false);///第一次创建,总容量为4
        }
        else if (fastpath(newOccupied + CACHE_END_MARKER <= capacity / 4 * 3)) { // 4  3 + 1 bucket cache_t
            // Cache is less than 3/4 full. Use it as-is.
        }
        else {
    ///不少于3/4的时候进行扩容
            capacity = capacity ? capacity * 2 : INIT_CACHE_SIZE;  // 扩容两倍 4
            if (capacity > MAX_CACHE_SIZE) {
                capacity = MAX_CACHE_SIZE;
            }
            reallocate(oldCapacity, capacity, true);  // 内存 库容完毕
        }
    
        bucket_t *b = buckets();
        mask_t m = capacity - 1;
        mask_t begin = cache_hash(sel, m);
        mask_t i = begin;
    
        // Scan for the first unused slot and insert there.
        // There is guaranteed to be an empty slot because the
        // minimum size is 4 and we resized at 3/4 full.
        do {
            if (fastpath(b[i].sel() == 0)) {//如果没找到方法就存值
                incrementOccupied();
                b[i].set<Atomic, Encoded>(sel, imp, cls);
                return;
            }
            if (b[i].sel() == sel) {
                // The entry was added to the cache by some other thread
                // before we grabbed the cacheUpdateLock.
                return;
            }
        } while (fastpath((i = cache_next(i, m)) != begin));//cache_next算法进行打乱了顺序
    
        cache_t::bad_cache(receiver, (SEL)sel, cls);
    }
    
    
    总结
    • _occupied就是实际的大小,_mask就是capacity - 1

    bucket为什么会丢失

    • 在重新申请内存时被清除掉了原有的

    为什么会乱序

    • 里面有哈希

    分析图

    Cooci 关于Cache_t原理分析图.png

    相关文章

      网友评论

          本文标题:六、cache_t分析

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