Cache详解

366 阅读2分钟

一、缓存Cache在哪?

缓存Cache存在类里面,从类占比第16个字节开始。 image.png 0x100c071d0就是Cache_t的内存地址。 image.png 取到cache_t地址 image.png 打印出来看,里面参数也不明白是什么意思,这时候我们就需要去看cache_t的数据结构。 image.png 截屏2022-04-29 08.53.07.png

cache_t数据结构如下,这样就跟上面的值对应起来了,但是每个字段是什么意思呢? image.png 先看一下这个数据结构在内存中占用多少内存? _bucketsAndMaybeMask 是一个 unsigned long类型的,占用8个字节。另一个是联合体,按占用最大空间的元素取,_originalPreoptCache占8个字节,所以这个联合体在内存中也占用8个字节。所以整个数据结构占用16个字节。 typedef unsigned long           uintptr_t; 再接着往下看,cache_t的方法,insert方法,在这个方法内就是具体的缓存实现,sel,imp,receiver. image.png image.png IMP和sel就是存在bucket_t内,看下面数据结构 image.png backet_t数据读取和存储 image.png image.png

void cache_t::insert(SEL sel, IMP imp, id receiver)
{
    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);

    }
    else if (fastpath(newOccupied + CACHE_END_MARKER <= cache_fill_ratio(capacity))) {

        // Cache is less than 3/4 or 7/8 full. Use it as-is.

    }
#if CACHE_ALLOW_FULL_UTILIZATION

    else if (capacity <= FULL_UTILIZATION_CACHE_SIZE && newOccupied + CACHE_END_MARKER <= capacity) {

        // Allow 100% cache utilization for small buckets. Use it as-is.

    }

// 当缓存内存超过后,则缓存扩容。
#endif
    else {
        capacity = capacity ? capacity * 2 : INIT_CACHE_SIZE;
        if (capacity > MAX_CACHE_SIZE) {
            capacity = MAX_CACHE_SIZE;
        }
        reallocate(oldCapacity, capacity, true);
    }
}

二、缓存的扩容

扩容机制:首先我们得知道你的cpu架构,不同的CPU架构对应的扩容机制是不一样的。 在 arm64 && !LP64 容量的初始值为4,其他架构下为2. image.png image.png

  1. 在arm架构下如果缓存大小 <= 总长度的7/8,不扩容

  2. 在x86架构下缓存大小+1 <= 总长度的3/4,不扩容

  3. 当总长度小于8的时候, 也不扩容。

  4. 否则扩容是当前容量的2倍。 image.png image.png

三、代码的优秀之处

简单的一句代码,分别说明了在arm架构,在x86架构下的规则说明,这就是源码的牛逼之处 image.png