前言
我们知道objc_class
有4个属性,第一个是从objc_object
继承过来的ISA
,是个objc_class *结构体指针,占8字节,superclass
同样是8字节的objc_class *指针,接下去是cache
和bits
,本文就cache
的结构和作用开始研究。
cache的结构分析
cache的大小
属性cache
的类型是cache_t
类型,查看cache_t
的源码,除去方法和静态变量,影响cache
的大小的有如下
cache_t
是个结构体,有两个属性:
- 第一个是一个8字节的指针
_bucketsAndMaybeMask
- 第二个是一个互斥的
联合体
联合体的大小:
8字节的指针_originalPreoptCache
和一个结构体
互斥,由联合体的特性也能推算结构体的大小也是8字节,所以联合体的大小是8字节,从而推出cache_t
的大小是16
个字节
联合体内包含的结构体根据不同的架构有不同的情况,我们选一种如图中标颜色的架构分析
- typedef uint32_t mask_t;
mask_t
是4
个字节 _occupied
是uint16_t,2
个字节_flags
是uint16_t,2
个字节- 所以加起来也是
8
个字节,验证了上述猜想
小结:cache的大小是16
个字节
cache的属性具体分析
_bucketsAndMaybeMask
顾名思义_bucketsAndMaybeMask
这个属性包含了buckets
,可能也包含mask
,那么一个8字节的指针是怎么存下buckets
和mask
的呢?带着这个疑问我们查看cache_t
里面的方法,找到了setBucketsAndMask
,全局搜索,我们发现这个方法有三个实现
这三个方法具体走哪个是根据架构区分的,查看架构的宏定义:
结合架构分析setBucketsAndMask
方法可以知道:
CACHE_MASK_STORAGE_OUTLINED
的架构_bucketsAndMaybeMask
存的就是buckets
,mask
是存在_mask
属性里面CACHE_MASK_STORAGE_HIGH_16
或者CACHE_MASK_STORAGE
的架构下maskShift
=48,所以_bucketsAndMaybeMask
的高16位存mask
,低48位存buckets
CACHE_MASK_STORAGE_LOW_4
的架构下高48位存buckets
,低16位存的是objc::mask16ShiftBits(mask)
mask16ShiftBits
方法是获取0xffff
需要右移多少位可以得到mask
至此我们终于明白_bucketsAndMaybeMask
其中maybe
的意思了,根据不同的架构会存buckets
,但是mask
存在与否可能就maybe
了。
_originalPreoptCache
全局搜索可以看到这些地方有用到这个属性
看代码注释这个变量大概是跟
共享缓存
或者是缓存的测试
有关,在libobjc
没看到别的地方的引用,看来一般情况是不会用到这个变量的,所以我们暂时先不重点研究这个变量了
_mask
capacity
顾名思义就是cache
的容量,mask
就是cache
的容量-1,当然mask
的获取是根据mask()
方法,也是根据架构不同要么直接拿_mask
的属性要么从_bucketsAndMaybeMask
这里面取,和存值是个反操作,这里不再赘述
_occupied
这个就一个地方实现,所有架构都一样,是cache
里面已经缓存的方法的个数。
cache的lldb调试
断点继续跑到
study4
前
这时候问题来了,我们明明跑了
init,study1,study2,study3
4个方法了,为什么_occupied
是2?
我们先打印下具体存了哪两个方法
这里我们打印
buckets
的第0个位置的内容发现sel
和imp
都是空的,这是为什么呢?
通过内存平移我们继续打印buckets
我们发现确实存在study2
和study3
这两个方法,所以_occupied
是2没问题,那么init,study1
这两个方法去哪去了,同时我们发现study2
和study3
的方法并不是顺序存的,那么buckets
一定不是我们理解的数组这样的连续存储的结构,可能有点像哈希数组的感觉。为了验证我们的猜想我们去找cache_t
对于方法的插入的函数,我们猜想是add
或者是insert
这样的方法,果然是这样的
cache的insert方法分析
void cache_t::insert(SEL sel, IMP imp, id receiver)
{
lockdebug::assert_locked(&runtimeLock);
// Never cache before +initialize is done
if (slowpath(!cls()->isInitialized())) {
return;
}
if (isConstantOptimizedCache()) {
_objc_fatal("cache_t::insert() called with a preoptimized cache for %s",
cls()->nameForLogging());
}
#if DEBUG_TASK_THREADS
return _collecting_in_critical();
#else
#if CONFIG_USE_CACHE_LOCK
mutex_locker_t lock(cacheUpdateLock);
#endif
ASSERT(sel != 0 && cls()->isInitialized());
// Use the cache as-is if until we exceed our expected fill ratio.
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);
}
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)) {
incrementOccupied();
b[i].set<Atomic, Encoded>(b, 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));
bad_cache(receiver, (SEL)sel);
#endif // !DEBUG_TASK_THREADS
}
新的方法缓存个数是原来的+1这是当然,毕竟新插入一个方法
第一次没缓存过方法会来这里面,根据架构不同初始化不同的容量,我这边的架构是
mac os
模拟器
capacity
=4
然后会分配
buckets
,根据freeOld
来回收内存,当然第一次不需要回收
CACHE_END_MARKER
=1
这段代码的含义是缓存的方法数+1如果超过了
3/4
的话需要扩容,当然不同的架构也可能是7/
扩容
扩容是2倍扩容
这段代码是关键
我们关注下
cache_hash
这个方法,第一个参数是sel
,第二个是capacity-1
也就是mask
看到这里我们通过哈希函数得到初始下标
begin
,前面对于buckets
的数据结构这里也得到了验证,其实就是哈希数组。
通过
cache_next
进行遍历
- 如果没有存过就存方法,并且
incrementOccupied
- 如果方法存在就直接返回
- 循环结束时又到了开始的方法
cache的模拟代码
不需要在libobjc
的源码环境,新建一个工程我们还原cache_t
的结构
模拟器下还原cache_t结构
struct sp_class_data_bits_t {
uintptr_t bits;
};
struct sp_bucket_t {
//真机环境是先_imp后_sel
SEL _sel;
IMP _imp;
};
struct sp_cache_t {
struct sp_bucket_t* _buckets; // 模拟器这个_bucketsAndMaybeMask其实就是_buckets,真机环境复杂点,留个读者探究
uint32_t _mask;
uint16_t _occupied;
uint16_t _flags;
};
struct sp_objc_class {
Class isa; // 注意这里的isa是继承父类的
Class superclass;
struct sp_cache_t cache;
struct sp_class_data_bits_t bits;
};
还原cache_t
的结构后我们可以通过桥接__bridge
把SPObject
类桥接成sp_objc_class
我们自己定义的结构体,这样我们可以分析每次调用方法时候_mask
,_occupied
,已经缓存的方法列表的变化
void test() {
struct sp_objc_class*sp_class = (__bridge struct sp_objc_class *)(SPObject.class);
NSLog(@"%u-%u- %u", sp_class->cache._mask,
sp_class->cache._occupied,
sp_class->cache._flags);
for(int i = 0; i < sp_class->cache._mask; i ++) {
struct sp_bucket_t bucket = sp_class->cache._buckets[i];
NSLog(@"%@-%p-%d", NSStringFromSelector(bucket._sel), bucket._imp, i);
}
NSLog(@"***************");
}
int main(int argc, const char * argv[]) {
@autoreleasepool {
SPObject *p = [[SPObject alloc] init];
test();
[p study1];
test();
[p study2];
test();
[p study3];
test();
[p study4];
test();
[p study5];
test();
[p study6];
test();
[p study7];
test();
}
return 0;
}
控制台输出如下:
**2023-01-10 22:53:01.719984+0800 KCObjcBuild[69345:16969273] 3-1- 32784**
**2023-01-10 22:53:01.723247+0800 KCObjcBuild[69345:16969273] init-0x8d5b748-0**
**2023-01-10 22:53:01.723601+0800 KCObjcBuild[69345:16969273] (null)-0x0-1**
**2023-01-10 22:53:01.723677+0800 KCObjcBuild[69345:16969273] (null)-0x0-2**
**2023-01-10 22:53:01.723737+0800 KCObjcBuild[69345:16969273] *****************
**2023-01-10 22:53:01.723870+0800 KCObjcBuild[69345:16969273] 3-2- 32784**
**2023-01-10 22:53:01.723993+0800 KCObjcBuild[69345:16969273] init-0x8d5b748-0**
**2023-01-10 22:53:01.724064+0800 KCObjcBuild[69345:16969273] study1-0xbf08-1**
**2023-01-10 22:53:01.724122+0800 KCObjcBuild[69345:16969273] (null)-0x0-2**
**2023-01-10 22:53:01.724177+0800 KCObjcBuild[69345:16969273] *****************
**2023-01-10 22:53:01.724250+0800 KCObjcBuild[69345:16969273] 7-1- 32784**
**2023-01-10 22:53:01.724320+0800 KCObjcBuild[69345:16969273] study2-0xbf18-0**
**2023-01-10 22:53:01.724379+0800 KCObjcBuild[69345:16969273] (null)-0x0-1**
**2023-01-10 22:53:01.724433+0800 KCObjcBuild[69345:16969273] (null)-0x0-2**
**2023-01-10 22:53:01.724494+0800 KCObjcBuild[69345:16969273] (null)-0x0-3**
**2023-01-10 22:53:01.724546+0800 KCObjcBuild[69345:16969273] (null)-0x0-4**
**2023-01-10 22:53:01.737091+0800 KCObjcBuild[69345:16969273] (null)-0x0-5**
**2023-01-10 22:53:01.737155+0800 KCObjcBuild[69345:16969273] (null)-0x0-6**
**2023-01-10 22:53:01.737200+0800 KCObjcBuild[69345:16969273] *****************
**2023-01-10 22:53:01.737297+0800 KCObjcBuild[69345:16969273] 7-2- 32784**
**2023-01-10 22:53:01.737367+0800 KCObjcBuild[69345:16969273] study2-0xbf18-0**
**2023-01-10 22:53:01.737422+0800 KCObjcBuild[69345:16969273] study3-0xbf68-1**
**2023-01-10 22:53:01.737468+0800 KCObjcBuild[69345:16969273] (null)-0x0-2**
**2023-01-10 22:53:01.737512+0800 KCObjcBuild[69345:16969273] (null)-0x0-3**
**2023-01-10 22:53:01.737556+0800 KCObjcBuild[69345:16969273] (null)-0x0-4**
**2023-01-10 22:53:01.737597+0800 KCObjcBuild[69345:16969273] (null)-0x0-5**
**2023-01-10 22:53:01.737638+0800 KCObjcBuild[69345:16969273] (null)-0x0-6**
**2023-01-10 22:53:01.737681+0800 KCObjcBuild[69345:16969273] *****************
**2023-01-10 22:53:01.737742+0800 KCObjcBuild[69345:16969273] 7-3- 32784**
**2023-01-10 22:53:01.737791+0800 KCObjcBuild[69345:16969273] study2-0xbf18-0**
**2023-01-10 22:53:01.749535+0800 KCObjcBuild[69345:16969273] study3-0xbf68-1**
**2023-01-10 22:53:01.749639+0800 KCObjcBuild[69345:16969273] (null)-0x0-2**
**2023-01-10 22:53:01.749687+0800 KCObjcBuild[69345:16969273] (null)-0x0-3**
**2023-01-10 22:53:01.749731+0800 KCObjcBuild[69345:16969273] (null)-0x0-4**
**2023-01-10 22:53:01.749776+0800 KCObjcBuild[69345:16969273] (null)-0x0-5**
**2023-01-10 22:53:01.749819+0800 KCObjcBuild[69345:16969273] study4-0xbf78-6**
**2023-01-10 22:53:01.749858+0800 KCObjcBuild[69345:16969273] *****************
**2023-01-10 22:53:01.749931+0800 KCObjcBuild[69345:16969273] 7-4- 32784**
**2023-01-10 22:53:01.749978+0800 KCObjcBuild[69345:16969273] study2-0xbf18-0**
**2023-01-10 22:53:01.750022+0800 KCObjcBuild[69345:16969273] study3-0xbf68-1**
**2023-01-10 22:53:01.750061+0800 KCObjcBuild[69345:16969273] (null)-0x0-2**
**2023-01-10 22:53:01.750099+0800 KCObjcBuild[69345:16969273] (null)-0x0-3**
**2023-01-10 22:53:01.750137+0800 KCObjcBuild[69345:16969273] (null)-0x0-4**
**2023-01-10 22:53:01.750180+0800 KCObjcBuild[69345:16969273] study5-0xbf48-5**
**2023-01-10 22:53:01.750222+0800 KCObjcBuild[69345:16969273] study4-0xbf78-6**
**2023-01-10 22:53:01.750259+0800 KCObjcBuild[69345:16969273] *****************
**2023-01-10 22:53:01.750315+0800 KCObjcBuild[69345:16969273] 7-5- 32784**
**2023-01-10 22:53:01.750360+0800 KCObjcBuild[69345:16969273] study2-0xbf18-0**
**2023-01-10 22:53:01.750449+0800 KCObjcBuild[69345:16969273] study3-0xbf68-1**
**2023-01-10 22:53:01.750522+0800 KCObjcBuild[69345:16969273] (null)-0x0-2**
**2023-01-10 22:53:01.750566+0800 KCObjcBuild[69345:16969273] (null)-0x0-3**
**2023-01-10 22:53:01.750729+0800 KCObjcBuild[69345:16969273] study6-0xbf58-4**
**2023-01-10 22:53:01.750802+0800 KCObjcBuild[69345:16969273] study5-0xbf48-5**
**2023-01-10 22:53:01.750849+0800 KCObjcBuild[69345:16969273] study4-0xbf78-6**
**2023-01-10 22:53:01.750903+0800 KCObjcBuild[69345:16969273] *****************
**2023-01-10 22:53:01.750981+0800 KCObjcBuild[69345:16969273] 15-1- 32784**
**2023-01-10 22:53:01.751078+0800 KCObjcBuild[69345:16969273] (null)-0x0-0**
**2023-01-10 22:53:01.751140+0800 KCObjcBuild[69345:16969273] (null)-0x0-1**
**2023-01-10 22:53:01.751184+0800 KCObjcBuild[69345:16969273] (null)-0x0-2**
**2023-01-10 22:53:01.751224+0800 KCObjcBuild[69345:16969273] (null)-0x0-3**
**2023-01-10 22:53:01.751264+0800 KCObjcBuild[69345:16969273] (null)-0x0-4**
**2023-01-10 22:53:01.751304+0800 KCObjcBuild[69345:16969273] (null)-0x0-5**
**2023-01-10 22:53:01.751343+0800 KCObjcBuild[69345:16969273] (null)-0x0-6**
**2023-01-10 22:53:01.751381+0800 KCObjcBuild[69345:16969273] (null)-0x0-7**
**2023-01-10 22:53:01.751421+0800 KCObjcBuild[69345:16969273] (null)-0x0-8**
**2023-01-10 22:53:01.751460+0800 KCObjcBuild[69345:16969273] (null)-0x0-9**
**2023-01-10 22:53:01.751500+0800 KCObjcBuild[69345:16969273] (null)-0x0-10**
**2023-01-10 22:53:01.751550+0800 KCObjcBuild[69345:16969273] study7-0xbea8-11**
**2023-01-10 22:53:01.751593+0800 KCObjcBuild[69345:16969273] (null)-0x0-12**
**2023-01-10 22:53:01.751632+0800 KCObjcBuild[69345:16969273] (null)-0x0-13**
**2023-01-10 22:53:01.751670+0800 KCObjcBuild[69345:16969273] (null)-0x0-14**
**2023-01-10 22:53:01.751708+0800 KCObjcBuild[69345:16969273] *****************
我们看到_mask
,_occupied
的变化是3-1,3-2,7-1,7-2,7-4,7-5,15-1
3-1
的解释:第一次方法调用,初始化capacity
=4, _mask
=capacity
-1=3, 并且缓存了一个方法init
3-1
到3-2
的解释:调用第二个方法2 + 1 <= 3 / 4 * 4
未达到扩容条件,所以容量不变,_mask
也不变还是3,同时新缓存了study1
方法,_occupied
变为2
3-2
到7-1
的解释:调用第三个方法3 + 1 > 3 / 4 * 4
满足扩容条件,2倍扩容``capacity
= 4 * 2 = 8, _mask
=capacity
-1=8-1=7,然后重新分配buckets
,调用reallocate
->setBucketsAndMask
,这时_occupied
=0,接着在新的buckets
(新开辟的内存空间,所以此时没有缓存方法)查找study2
,未找到,于是插入方法,同时incrementOccupied
,这时这时_occupied
=1 所以这就是7-1
的来源
7-5
到15-1
的解释:调用study7
方法 6 + 1 > 3 / 4 * 8
满足扩容条件,2倍扩容``capacity
= 8 * 2 = 16, _mask
=capacity
-1=16-1=15,然后重新分配buckets
,调用eallocate
->setBucketsAndMask
,这时_occupied=
0,接着在新的buckets
(新开辟的内存空间,所以此时没有缓存方法)查找study7
,未找到,于是插入方法,同时incrementOccupied
,这时这时_occupied
=1 所以这就是15-1
的来源 我们看到study7
存在的位置是11,也就是通过hash_cache
算出来的下标
真机下还原cache_t结构
struct sp_zhenji_bucket_t {
//真机先imp后sel
IMP _imp;
SEL _sel;
};
struct sp_zhenji_cache_t {
// 真机架构
unsigned long _bucketsAndMaybeMask;
uint32_t _unused;
uint16_t _occupied;
uint16_t _flags;
struct sp_zhenji_bucket_t *buckets() {
//低44位存buckests,中间4位是maskZeroBits
return (sp_zhenji_bucket_t *)(_bucketsAndMaybeMask << 20 >> 20);
}
uint32_t mask() {
//高16位存mask
return _bucketsAndMaybeMask >> 48;
}
};
struct sp_zhenji_objc_class {
Class isa;
Class superclass;
struct sp_zhenji_cache_t cache;
struct sp_class_data_bits_t bits;
};
改造下模拟器的测试代码我们可以得到真机的测试代码如下:
void test_zhenji() {
struct sp_zhenji_objc_class* sp_class = (__bridge struct sp_zhenji_objc_class *)(SPObject.class);
NSLog(@"%u-%u", sp_class->cache.mask(),
sp_class->cache._occupied);
for(int i = 0; i <= sp_class->cache.mask(); i ++) {
struct sp_zhenji_bucket_t bucket = sp_class->cache.buckets()[i];
NSLog(@"%@-%p-%d", NSStringFromSelector(bucket._sel), bucket._imp, i);
}
NSLog(@"***************");
}
SPObject *p = [[SPObject alloc] init];
test_zhenji();
[p study1];
test_zhenji();
[p study2];
test_zhenji();
[p study3];
test_zhenji();
[p study4];
test_zhenji();
[p study5];
test_zhenji();
[p study6];
test_zhenji();
[p study7];
test_zhenji();
[p study1];
test_zhenji();
[p study2];
test_zhenji();
[p study3];
test_zhenji();
[p study4];
test_zhenji();
[p study5];
test_zhenji();
[p study8];
test_zhenji();
[p study9];
test_zhenji();
运行代码可以得到如下结果:
2023-02-18 15:20:31.981278+0800 TestCache[672:70223] 1-1
2023-02-18 15:20:31.981334+0800 TestCache[672:70223] init-0x240f84019f4523e8-0
2023-02-18 15:20:31.981355+0800 TestCache[672:70223] (null)-0x0-1
2023-02-18 15:20:31.981370+0800 TestCache[672:70223] ***************
2023-02-18 15:20:31.981383+0800 TestCache[672:70223] 1-2
2023-02-18 15:20:31.981403+0800 TestCache[672:70223] init-0x240f84019f4523e8-0
2023-02-18 15:20:31.981423+0800 TestCache[672:70223] study1-0xa02d96010283de70-1
2023-02-18 15:20:31.981438+0800 TestCache[672:70223] ***************
2023-02-18 15:20:31.981451+0800 TestCache[672:70223] 3-1
2023-02-18 15:20:31.981464+0800 TestCache[672:70223] (null)-0x0-0
2023-02-18 15:20:31.981477+0800 TestCache[672:70223] (null)-0x0-1
2023-02-18 15:20:31.981501+0800 TestCache[672:70223] study2-0x4630c2010283de84-2
2023-02-18 15:20:31.981516+0800 TestCache[672:70223] (null)-0x0-3
2023-02-18 15:20:31.981529+0800 TestCache[672:70223] ***************
2023-02-18 15:20:31.981541+0800 TestCache[672:70223] 3-2
2023-02-18 15:20:31.991736+0800 TestCache[672:70223] (null)-0x0-0
2023-02-18 15:20:31.991763+0800 TestCache[672:70223] study3-0x3602cc810283de98-1
2023-02-18 15:20:31.991787+0800 TestCache[672:70223] study2-0x4630c2010283de84-2
2023-02-18 15:20:31.991803+0800 TestCache[672:70223] (null)-0x0-3
2023-02-18 15:20:31.991817+0800 TestCache[672:70223] ***************
2023-02-18 15:20:31.991832+0800 TestCache[672:70223] 3-3
2023-02-18 15:20:31.991853+0800 TestCache[672:70223] study4-0xce7878010283deac-0
2023-02-18 15:20:31.991876+0800 TestCache[672:70223] study3-0x3602cc810283de98-1
2023-02-18 15:20:31.991899+0800 TestCache[672:70223] study2-0x4630c2010283de84-2
2023-02-18 15:20:31.991914+0800 TestCache[672:70223] (null)-0x0-3
2023-02-18 15:20:31.991929+0800 TestCache[672:70223] ***************
2023-02-18 15:20:31.991943+0800 TestCache[672:70223] 3-4
2023-02-18 15:20:31.991962+0800 TestCache[672:70223] study4-0xce7878010283deac-0
2023-02-18 15:20:31.991983+0800 TestCache[672:70223] study3-0x3602cc810283de98-1
2023-02-18 15:20:31.995589+0800 TestCache[672:70223] study2-0x4630c2010283de84-2
2023-02-18 15:20:31.995616+0800 TestCache[672:70223] study5-0x9415be010283dec0-3
2023-02-18 15:20:31.995632+0800 TestCache[672:70223] ***************
2023-02-18 15:20:31.995647+0800 TestCache[672:70223] 7-1
2023-02-18 15:20:31.995660+0800 TestCache[672:70223] (null)-0x0-0
2023-02-18 15:20:31.995673+0800 TestCache[672:70223] (null)-0x0-1
2023-02-18 15:20:31.995693+0800 TestCache[672:70223] study6-0x14913810283ded4-2
2023-02-18 15:20:31.995708+0800 TestCache[672:70223] (null)-0x0-3
2023-02-18 15:20:31.995739+0800 TestCache[672:70223] (null)-0x0-4
2023-02-18 15:20:31.995753+0800 TestCache[672:70223] (null)-0x0-5
2023-02-18 15:20:31.995766+0800 TestCache[672:70223] (null)-0x0-6
2023-02-18 15:20:31.995779+0800 TestCache[672:70223] (null)-0x0-7
2023-02-18 15:20:31.995792+0800 TestCache[672:70223] ***************
2023-02-18 15:20:31.995805+0800 TestCache[672:70223] 7-2
2023-02-18 15:20:31.995818+0800 TestCache[672:70223] (null)-0x0-0
2023-02-18 15:20:31.997287+0800 TestCache[672:70223] (null)-0x0-1
2023-02-18 15:20:31.997311+0800 TestCache[672:70223] study6-0x14913810283ded4-2
2023-02-18 15:20:31.997325+0800 TestCache[672:70223] (null)-0x0-3
2023-02-18 15:20:31.997340+0800 TestCache[672:70223] (null)-0x0-4
2023-02-18 15:20:31.997364+0800 TestCache[672:70223] study7-0xfd311d010283dee8-5
2023-02-18 15:20:31.997378+0800 TestCache[672:70223] (null)-0x0-6
2023-02-18 15:20:31.997391+0800 TestCache[672:70223] (null)-0x0-7
2023-02-18 15:20:31.997403+0800 TestCache[672:70223] ***************
2023-02-18 15:20:31.997416+0800 TestCache[672:70223] 7-3
2023-02-18 15:20:31.997431+0800 TestCache[672:70223] (null)-0x0-0
2023-02-18 15:20:31.997445+0800 TestCache[672:70223] (null)-0x0-1
2023-02-18 15:20:31.997466+0800 TestCache[672:70223] study6-0x14913810283ded4-2
2023-02-18 15:20:31.997481+0800 TestCache[672:70223] (null)-0x0-3
2023-02-18 15:20:31.997501+0800 TestCache[672:70223] study1-0x3a68a5810283de70-4
2023-02-18 15:20:31.997524+0800 TestCache[672:70223] study7-0xfd311d010283dee8-5
2023-02-18 15:20:31.997538+0800 TestCache[672:70223] (null)-0x0-6
2023-02-18 15:20:31.997552+0800 TestCache[672:70223] (null)-0x0-7
2023-02-18 15:20:31.997565+0800 TestCache[672:70223] ***************
2023-02-18 15:20:31.999023+0800 TestCache[672:70223] 7-4
2023-02-18 15:20:31.999040+0800 TestCache[672:70223] (null)-0x0-0
2023-02-18 15:20:31.999053+0800 TestCache[672:70223] (null)-0x0-1
2023-02-18 15:20:31.999074+0800 TestCache[672:70223] study6-0x14913810283ded4-2
2023-02-18 15:20:31.999090+0800 TestCache[672:70223] (null)-0x0-3
2023-02-18 15:20:31.999110+0800 TestCache[672:70223] study1-0x3a68a5810283de70-4
2023-02-18 15:20:31.999133+0800 TestCache[672:70223] study7-0xfd311d010283dee8-5
2023-02-18 15:20:31.999189+0800 TestCache[672:70223] study2-0x7248e6810283de84-6
2023-02-18 15:20:31.999204+0800 TestCache[672:70223] (null)-0x0-7
2023-02-18 15:20:31.999218+0800 TestCache[672:70223] ***************
2023-02-18 15:20:31.999231+0800 TestCache[672:70223] 7-5
2023-02-18 15:20:31.999244+0800 TestCache[672:70223] (null)-0x0-0
2023-02-18 15:20:31.999264+0800 TestCache[672:70223] study3-0x300fee810283de98-1
2023-02-18 15:20:31.999284+0800 TestCache[672:70223] study6-0x14913810283ded4-2
2023-02-18 15:20:31.999298+0800 TestCache[672:70223] (null)-0x0-3
2023-02-18 15:20:31.999320+0800 TestCache[672:70223] study1-0x3a68a5810283de70-4
2023-02-18 15:20:31.999340+0800 TestCache[672:70223] study7-0xfd311d010283dee8-5
2023-02-18 15:20:31.999362+0800 TestCache[672:70223] study2-0x7248e6810283de84-6
2023-02-18 15:20:31.999379+0800 TestCache[672:70223] (null)-0x0-7
2023-02-18 15:20:31.999394+0800 TestCache[672:70223] ***************
2023-02-18 15:20:32.001303+0800 TestCache[672:70223] 7-6
2023-02-18 15:20:32.001327+0800 TestCache[672:70223] study4-0x2e7a3e010283deac-0
2023-02-18 15:20:32.001352+0800 TestCache[672:70223] study3-0x300fee810283de98-1
2023-02-18 15:20:32.001378+0800 TestCache[672:70223] study6-0x14913810283ded4-2
2023-02-18 15:20:32.001394+0800 TestCache[672:70223] (null)-0x0-3
2023-02-18 15:20:32.001414+0800 TestCache[672:70223] study1-0x3a68a5810283de70-4
2023-02-18 15:20:32.001434+0800 TestCache[672:70223] study7-0xfd311d010283dee8-5
2023-02-18 15:20:32.001454+0800 TestCache[672:70223] study2-0x7248e6810283de84-6
2023-02-18 15:20:32.001469+0800 TestCache[672:70223] (null)-0x0-7
2023-02-18 15:20:32.001483+0800 TestCache[672:70223] ***************
2023-02-18 15:20:32.001496+0800 TestCache[672:70223] 7-7
2023-02-18 15:20:32.001517+0800 TestCache[672:70223] study4-0x2e7a3e010283deac-0
2023-02-18 15:20:32.001540+0800 TestCache[672:70223] study3-0x300fee810283de98-1
2023-02-18 15:20:32.001561+0800 TestCache[672:70223] study6-0x14913810283ded4-2
2023-02-18 15:20:32.001580+0800 TestCache[672:70223] study5-0xd00257010283dec0-3
2023-02-18 15:20:32.003027+0800 TestCache[672:70223] study1-0x3a68a5810283de70-4
2023-02-18 15:20:32.003049+0800 TestCache[672:70223] study7-0xfd311d010283dee8-5
2023-02-18 15:20:32.003070+0800 TestCache[672:70223] study2-0x7248e6810283de84-6
2023-02-18 15:20:32.003085+0800 TestCache[672:70223] (null)-0x0-7
2023-02-18 15:20:32.003099+0800 TestCache[672:70223] ***************
2023-02-18 15:20:32.003114+0800 TestCache[672:70223] 7-8
2023-02-18 15:20:32.003136+0800 TestCache[672:70223] study4-0x2e7a3e010283deac-0
2023-02-18 15:20:32.003157+0800 TestCache[672:70223] study3-0x300fee810283de98-1
2023-02-18 15:20:32.003179+0800 TestCache[672:70223] study6-0x14913810283ded4-2
2023-02-18 15:20:32.003200+0800 TestCache[672:70223] study5-0xd00257010283dec0-3
2023-02-18 15:20:32.003221+0800 TestCache[672:70223] study1-0x3a68a5810283de70-4
2023-02-18 15:20:32.003241+0800 TestCache[672:70223] study7-0xfd311d010283dee8-5
2023-02-18 15:20:32.003262+0800 TestCache[672:70223] study2-0x7248e6810283de84-6
2023-02-18 15:20:32.003283+0800 TestCache[672:70223] study8-0x4c7f40810283defc-7
2023-02-18 15:20:32.003298+0800 TestCache[672:70223] ***************
2023-02-18 15:20:32.003317+0800 TestCache[672:70223] 15-1
2023-02-18 15:20:32.003331+0800 TestCache[672:70223] (null)-0x0-0
2023-02-18 15:20:32.003346+0800 TestCache[672:70223] (null)-0x0-1
2023-02-18 15:20:32.003359+0800 TestCache[672:70223] (null)-0x0-2
2023-02-18 15:20:32.003373+0800 TestCache[672:70223] (null)-0x0-3
2023-02-18 15:20:32.003542+0800 TestCache[672:70223] (null)-0x0-4
2023-02-18 15:20:32.003558+0800 TestCache[672:70223] (null)-0x0-5
2023-02-18 15:20:32.003571+0800 TestCache[672:70223] (null)-0x0-6
2023-02-18 15:20:32.003592+0800 TestCache[672:70223] study9-0xc5461a010283df10-7
2023-02-18 15:20:32.003606+0800 TestCache[672:70223] (null)-0x0-8
2023-02-18 15:20:32.003619+0800 TestCache[672:70223] (null)-0x0-9
2023-02-18 15:20:32.003633+0800 TestCache[672:70223] (null)-0x0-10
2023-02-18 15:20:32.003646+0800 TestCache[672:70223] (null)-0x0-11
2023-02-18 15:20:32.003660+0800 TestCache[672:70223] (null)-0x0-12
2023-02-18 15:20:32.003674+0800 TestCache[672:70223] (null)-0x0-13
2023-02-18 15:20:32.003688+0800 TestCache[672:70223] (null)-0x0-14
2023-02-18 15:20:32.003701+0800 TestCache[672:70223] (null)-0x0-15
2023-02-18 15:20:32.003715+0800 TestCache[672:70223] ***************
我们看到_mask
,_occupied
的变化是1-1,1-2,3-1,3-2,3-3,3-4,7-1,7-2,7-3,7-4,7-5,7-6,7-7,7-8,15-1
- 开始是
1-1
的原因:真机环境初始化容量INIT_CACHE_SIZE
定义为2
,所以_mask=2-1=1
- 真机环境
CACHE_END_MARKER
的值为0
,CACHE_ALLOW_FULL_UTILIZATION
的值为1
结论:真机的初始化容量是2
,在容量小于等于8
的时候是满扩容,超过8
的时候才会触发3/4
扩容,这也就解释了_mask
,_occupied
从7-8
到15-1
变化的原因
总结
cache
的大小是16个
字节,分别是8
字节的指针_bucketsAndMaybeMask
和8
字节的指针_originalPreoptCache
_bucketsAndMaybeMask
在CACHE_MASK_STORAGE_OUTLINED
的架构下存的就是buckets
,其他架构会存mask
(高16位或者低16位mask16ShiftBits
)_mask
=capacity
-1 是cache的容量-1_occupied
是已缓存的方法数_originalPreoptCache
可能跟共享缓存或者是缓存的测试有关- 重点是cache的
insert
方法 - 初始化
INIT_CACHE_SIZE
大小的容量,此时开辟内存不用freeOld
3/4
或者7/8
扩容(架构不同)2倍扩容
,扩容是新开辟内存,free掉原来的内存- 通过
哈希函数
hash_cache获取下标 - 哈希冲突的解决是
再哈希
取下个下标 - 为什么是哈希结构(如果是普通的数组结构每次方法判断存在与否需要O(n)的时间复杂度,而用哈希数组可以在O(1)的时间复杂度获取下标)
- 3/4其实是
负载因子
的概念,值越大的情况空间利用率高,但是更容易哈希冲突,查找时间变久;值越小,更快的需要进行开辟新的内存空间,空间利用率低,当然哈希冲突概率减小,查找时间变快。3/4是时间和空间利用率都比较不错的 - 真机的初始化容量是
2
,在容量小于等于8
的时候是满扩容,超过8
的时候才会触发3/4
扩容