分析cache
cache
的存
struct cache_t {
static bucket_t *emptyBuckets(); // 清空
static bucket_t *allocateBuckets(mask_t newCapacity); //创建
static bucket_t *emptyBucketsForCapacity(mask_t capacity, bool allocate = true); //清空固定大小
void insert(SEL sel, IMP imp, id receiver); // 存缓存的方法
struct bucket_t *buckets() const; //核心数据类型
}
// insert方法
void cache_t::insert(SEL sel, IMP imp, id receiver) {
...
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); // 创建缓存
}
...
}
// 缓存肯定是没有的时候创建,那么这个`reallocate`方法肯定就是创建了
void cache_t::reallocate(mask_t oldCapacity, mask_t newCapacity, bool freeOld)
{
bucket_t *oldBuckets = buckets();
bucket_t *newBuckets = allocateBuckets(newCapacity);
// Cache's old contents are not propagated.
// This is thought to save cache memory at the cost of extra cache fills.
// fixme re-measure this
ASSERT(newCapacity > 0);
ASSERT((uintptr_t)(mask_t)(newCapacity-1) == newCapacity-1);
setBucketsAndMask(newBuckets, newCapacity - 1);
if (freeOld) {
collect_free(oldBuckets, oldCapacity);
}
}
在insert
中发现了哈希
取bucket_t
:
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.
do {
if (fastpath(b[i].sel() == 0)) { // 如果此位置的sel()为空,就进行赋值
incrementOccupied(); // _occupied++, 使用数+1
b[i].set<Atomic, Encoded>(b, sel, imp, cls()); //将sel和imp分别存入bucket的_sel和_imp中
return;
}
// 如果当前位置的sel等于要插入的sel,则返回
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));
//这个`while`循环是在`b`中找到第一个`sel为0`处,然后在这个位置将`sel和imp`分别存到`butket`的`_sel和_imp`中。`b[i].sel()`中,可断定`b[i]`是`butket`
存入缓存的核心是inset
void cache_t::insert(SEL sel, IMP imp, id receiver) {
...
//第一次使用时,没有缓存,则occupied为0
mask_t newOccupied = occupied() + 1;
// 第一次没有缓存,所以 oldCapacity = 0,capacity = 0
unsigned oldCapacity = capacity(), capacity = oldCapacity;
if (slowpath(isConstantEmptyCache())) { // 第一次没有缓存,则进入创建方法
// Cache is read-only. Replace it.
if (!capacity) capacity = INIT_CACHE_SIZE; //INIT_CACHE_SIZE = (1 << INIT_CACHE_SIZE_LOG2) 为4
reallocate(oldCapacity, capacity, /* freeOld */false); // 0,4,false // 开辟内存
}
else if (fastpath(newOccupied + CACHE_END_MARKER <= cache_fill_ratio(capacity))) {
// CACHE_END_MARKER = 1
// 第一次判断:newOccupied = 1,capacity = 4, 1+1 < 3*4/4,无其他操作
// 第二次判断:newOccupied = 1+1=2,capacity = 4,2+1=3*4/4,无其他操作
// 第三次判断:newOccupied = 2+1 = 3,capacity=4,3+1 > 3 * 4 / 4,不满足,走else
// 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.
} //- 在`small bucket`中,可以存满
#endif
else {
// - 当容量大于总容量`3/4`时,会进行扩容,总容量为之前的`2倍`,最大容量不能超过`2^15`
capacity = capacity ? capacity * 2 : INIT_CACHE_SIZE;
// MAX_CACHE_SIZE = 1<<16 = 2^5 = 32
if (capacity > MAX_CACHE_SIZE) {
capacity = MAX_CACHE_SIZE;
}
reallocate(oldCapacity, capacity, true); //- 扩容后创建个新`bucket`,然后释放旧的`bucket`内存
}
// 缓存
bucket_t *b = buckets(); //拿到`bucket`地址
mask_t m = capacity - 1; // 计算能存储的位置
mask_t begin = cache_hash(sel, m); //拿到hash下标
mask_t i = begin;
// Scan for the first unused slot and insert there.
// There is guaranteed to be an empty slot.
do {
if (fastpath(b[i].sel() == 0)) { // 如果此位置的sel()为空,就进行赋值
incrementOccupied(); // _occupied++, 使用数+1
b[i].set<Atomic, Encoded>(b, sel, imp, cls()); //将sel和imp分别存入bucket的_sel和_imp中
return;
}
// 如果当前位置的sel等于要插入的sel,则返回
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));
/**
- 1. 根据内存订单`memory_order`拿到`bucket`地址
- 2. 根据`sel`和`mask`计算出起始位置
- 3. 通过遍历在`bucket`中寻找`sel为空`的位置,并在这个位置`对插入的sel和imp进行存储`,然后进行进行`_occupied`自增。
*、
}
- 第一次调用时,
capacity = INIT_CACHE_SIZE = 4
,再调用reallocate
开辟:
// 第一次,oldCapacity:0,newCapacity:4,freeOld:false
void cache_t::reallocate(mask_t oldCapacity, mask_t newCapacity, bool freeOld)
{
bucket_t *oldBuckets = buckets(); //加载旧的buckets
bucket_t *newBuckets = allocateBuckets(newCapacity); //创建新的
// Cache's old contents are not propagated.
// This is thought to save cache memory at the cost of extra cache fills.
// fixme re-measure this
ASSERT(newCapacity > 0);
ASSERT((uintptr_t)(mask_t)(newCapacity-1) == newCapacity-1);
setBucketsAndMask(newBuckets, newCapacity - 1);
if (freeOld) {
collect_free(oldBuckets, oldCapacity);
}
}
reallocate
核心步骤是:
-
allocateBuckets
:开辟内存创建新bucket
bytesForCapacity(newCapacity)
开辟内存- 计算出
bucket
地址,也就是最后一个bucket
的地址,然后将bucket
赋值,sel=1
,imp=newBuckets
// 第一次,newCapacity:4
bucket_t *cache_t::allocateBuckets(mask_t newCapacity)
{
// Allocate one extra bucket to mark the end of the list.
// This can't overflow mask_t because newCapacity is a power of 2.
bucket_t *newBuckets = (bucket_t *)calloc(bytesForCapacity(newCapacity), 1); // 根据计算出的内存,创建bucket
bucket_t *end = endMarker(newBuckets, newCapacity); //根据新bucket起始位置,获取最后一个butket的地址
#if __arm__
// End marker's sel is 1 and imp points BEFORE the first bucket.
// This saves an instruction in objc_msgSend.
end->set<NotAtomic, Raw>(newBuckets, (SEL)(uintptr_t)1, (IMP)(newBuckets - 1), nil);
#else
// End marker's sel is 1 and imp points to the first bucket.
// 最后一个 bucket赋值:sel = 1,imp = newBuckets
end->set<NotAtomic, Raw>(newBuckets, (SEL)(uintptr_t)1, (IMP)newBuckets, nil);
#endif
if (PrintCaches) recordNewCache(newCapacity); //记录缓存
return newBuckets;
}
size_t cache_t::bytesForCapacity(uint32_t cap)
{
return sizeof(bucket_t) * cap; //根据容量,计算内存
}
#if CACHE_END_MARKER // 模拟器
bucket_t *cache_t::endMarker(struct bucket_t *b, uint32_t cap)
{
return (bucket_t *)((uintptr_t)b + bytesForCapacity(cap)) - 1; //根据新bucket大小,
}
-
setBucketsAndMask
:存储_maybeMask
,设置_occupied
,_bucketsAndMaybeMask
存储新bucket
。
-
_bucketsAndMaybeMask
存储newBuckets
,根据架构不同存储newBuckets
时的key
不同
-
_maybeMask
存储newMask
-
- 存储完
_occupied
置为0
。
- 存储完
// 第一次 `newMask` 为3
void cache_t::setBucketsAndMask(struct bucket_t *newBuckets, mask_t newMask)
{
// objc_msgSend uses mask and buckets with no locks.
// It is safe for objc_msgSend to see new buckets but old mask.
// (It will get a cache miss but not overrun the buckets' bounds).
// It is unsafe for objc_msgSend to see old buckets and new mask.
// Therefore we write new buckets, wait a lot, then write new mask.
// objc_msgSend reads mask first, then buckets.
#ifdef __arm__
// ensure other threads see buckets contents before buckets pointer
mega_barrier();
_bucketsAndMaybeMask.store((uintptr_t)newBuckets, memory_order_relaxed);
// ensure other threads see new buckets before new mask
mega_barrier();
_maybeMask.store(newMask, memory_order_relaxed);
_occupied = 0;
#elif __x86_64__ || i386
// ensure other threads see buckets contents before buckets pointer
_bucketsAndMaybeMask.store((uintptr_t)newBuckets, memory_order_release);
// ensure other threads see new buckets before new mask
_maybeMask.store(newMask, memory_order_release);
_occupied = 0;
#else
#error Don't know how to do setBucketsAndMask on this architecture.
#endif
}
-
collect_free
:是否释放旧内存。
void cache_t::collect_free(bucket_t *data, mask_t capacity)
{
#if CONFIG_USE_CACHE_LOCK
cacheUpdateLock.assertLocked();
#else
runtimeLock.assertLocked();
#endif
if (PrintCaches) recordDeadCache(capacity);
_garbage_make_room (); // 创建回收站
garbage_byte_size += cache_t::bytesForCapacity(capacity); //获取回收的`bucket`大小,并加到garbage_byte_size上
garbage_refs[garbage_count++] = data;
cache_t::collectNolock(false);
}
// 创建回收站,如果回收站满了会重新分配`2倍`空间:_garbage_make_room
static void _garbage_make_room(void)
{
static int first = 1;
// Create the collection table the first time it is needed
if (first)
{
// 第一次调用创建回收站内存
first = 0;
garbage_refs = (bucket_t**)malloc(INIT_GARBAGE_COUNT * sizeof(void *));
garbage_max = INIT_GARBAGE_COUNT; //设置最大存储量
}
// Double the table if it is full
// 如果存储满了,就重新分配内存,最大的内存为之前的2倍
else if (garbage_count == garbage_max)
{
garbage_refs = (bucket_t**)realloc(garbage_refs, garbage_max * 2 * sizeof(void *));
garbage_max *= 2;
}
}
// - 2. `garbage_refs`:将当前回收`bucket`放在回收站`上一个回收后`的位置上。
// - 3. `collectNolock`:清空数据,回收内存