上一篇 内存管理 中我们了解到,对象的内存管理有tagged pointer这种无需引用计数的,也有需要引用计数的;而当 指针为非nonpointerIsa、或者nonpointerIsa中的 extra_rc 容量不够存放引用计数时,会使用 散列表sidetable 来协助存储,本篇就来探索一下 sidetable
sidetable(散列表)
struct SideTable {
spinlock_t slock;
// 存放从extra_rc接收的那一半引用计数
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);
};
- 主要包含一把
spinlock锁、引用计数表和弱引用表
1、RefcountMap(引用计数表)
-
RefcountMap 本身从
DenseMap得来,这个东西很眼熟,就是在探索 关联对象 时打过交道的,就是hash表typedef objc::DenseMap<DisguisedPtr<objc_object>,size_t,RefcountMapValuePurgeable> RefcountMap; -
存放从 extra_rc 接收的那一半引用计数
if (variant == RRVariant::Full) { if (slowpath(transcribeToSideTable)) { // Copy the other half of the retain counts to the side table. // 将引用计数一半存在散列表中的方法 sidetable_addExtraRC_nolock(RC_HALF); } if (slowpath(!tryRetain && sideTableLocked)) sidetable_unlock(); } else { ASSERT(!transcribeToSideTable); ASSERT(!sideTableLocked); }sidetable_addExtraRC_nolock:
bool objc_object::sidetable_addExtraRC_nolock(size_t delta_rc) { ASSERT(isa.nonpointer); // 获取SideTables,也就是StripeMap SideTable& table = SideTables()[this]; size_t& refcntStorage = table.refcnts[this]; size_t oldRefcnt = refcntStorage; // isa-side bits should not be set here ASSERT((oldRefcnt & SIDE_TABLE_DEALLOCATING) == 0); ASSERT((oldRefcnt & SIDE_TABLE_WEAKLY_REFERENCED) == 0); if (oldRefcnt & SIDE_TABLE_RC_PINNED) return true; uintptr_t carry; size_t newRefcnt = addc(oldRefcnt, delta_rc << SIDE_TABLE_RC_SHIFT, 0, &carry); if (carry) { refcntStorage = SIDE_TABLE_RC_PINNED | (oldRefcnt & SIDE_TABLE_FLAG_MASK); return true; } else { refcntStorage = newRefcnt; return false; } } -
系统中使用 spinlock_t 加锁的内容,因为频繁读取操作会对性能产生影响,所以经常使用多张
StripedMap来分散压力,且 StripedMap 数量根据系统数量固定,这是探索 @synchronized 是知道的// 上面 SideTables 的实现 static StripedMap<SideTable>& SideTables() { return SideTablesMap.get(); }
2、WeakTable(弱引用表)
弱引用底层会调用objc_initWeak
id
objc_initWeak(id *location, id newObj)
{
if (!newObj) {
*location = nil;
return nil;
}
return storeWeak<DontHaveOld, DoHaveNew, DoCrashIfDeallocating>
(location, (objc_object*)newObj);
}
2.1、storeWeak
enum CrashIfDeallocating {
DontCrashIfDeallocating = false, DoCrashIfDeallocating = true
};
// HaveOld:weak指针是否指向一个弱引用
// HavNew:weak指针是否需要指向一个新的引用
template <HaveOld haveOld, HaveNew haveNew,
enum CrashIfDeallocating crashIfDeallocating>
static id
storeWeak(id *location, objc_object *newObj)
{
ASSERT(haveOld || haveNew);
if (!haveNew) ASSERT(newObj == nil);
Class previouslyInitializedClass = nil;
id oldObj;
SideTable *oldTable;
SideTable *newTable;
// Acquire locks for old and new values.
// Order by lock address to prevent lock ordering problems.
// Retry if the old value changes underneath us.
retry:
if (haveOld) {
oldObj = *location;
oldTable = &SideTables()[oldObj];
} else {
oldTable = nil;
}
if (haveNew) {
newTable = &SideTables()[newObj];
} else {
newTable = nil;
}
SideTable::lockTwo<haveOld, haveNew>(oldTable, newTable);
if (haveOld && *location != oldObj) {
SideTable::unlockTwo<haveOld, haveNew>(oldTable, newTable);
goto retry;
}
// Prevent a deadlock between the weak reference machinery
// and the +initialize machinery by ensuring that no
// weakly-referenced object has an un-+initialized isa.
if (haveNew && newObj) {
Class cls = newObj->getIsa();
if (cls != previouslyInitializedClass &&
!((objc_class *)cls)->isInitialized())
{
SideTable::unlockTwo<haveOld, haveNew>(oldTable, newTable);
class_initialize(cls, (id)newObj);
// If this class is finished with +initialize then we're good.
// If this class is still running +initialize on this thread
// (i.e. +initialize called storeWeak on an instance of itself)
// then we may proceed but it will appear initializing and
// not yet initialized to the check above.
// Instead set previouslyInitializedClass to recognize it on retry.
previouslyInitializedClass = cls;
goto retry;
}
}
// Clean up old value, if any.
if (haveOld) {
// 清除原来弱引用表中数据
weak_unregister_no_lock(&oldTable->weak_table, oldObj, location);
}
// Assign new value, if any.
if (haveNew) {
newObj = (objc_object *)
// 将weak的指针地址添加到对象的弱引用表
weak_register_no_lock(&newTable->weak_table, (id)newObj, location,
crashIfDeallocating ? CrashIfDeallocating : ReturnNilIfDeallocating);
// weak_register_no_lock returns nil if weak store should be rejected
// Set is-weakly-referenced bit in refcount table.
if (!newObj->isTaggedPointerOrNil()) {
// 将对象曾经指向过若引用的标识置为true,没有弱引用的释放更快
newObj->setWeaklyReferenced_nolock();
}
// Do not set *location anywhere else. That would introduce a race.
*location = (id)newObj;
}
else {
// No new value. The storage is not changed.
}
SideTable::unlockTwo<haveOld, haveNew>(oldTable, newTable);
// This must be called without the locks held, as it can invoke
// arbitrary code. In particular, even if _setWeaklyReferenced
// is not implemented, resolveInstanceMethod: may be, and may
// call back into the weak reference machinery.
callSetWeaklyReferenced((id)newObj);
return (id)newObj;
}
- storeWeak 一共有5个参数,其中3个参数定义在了 template模板,在探索类时我们也遇到过(property、method、protocol的模板)
- weak_unregister_no_lock:清除原来弱引用表中数据
- weak_register_no_lock:将weak的指针地址添加到对象的弱引用表
2.2、weak_entry_t
struct weak_table_t {
// 弱引用数组
weak_entry_t *weak_entries;
// 数组个数
size_t num_entries;
uintptr_t mask;
uintptr_t max_hash_displacement;
};
- weak_entries:哈希数组,一个对象可以被多个弱引用指针引用,因此这里是个数组
struct weak_entry_t {
DisguisedPtr<objc_object> referent;
// 联合体,对象的弱引用个数若小于等于4,下边的结构体:静态存储,大于4,上边的结构体:动态存储
union {
struct {
weak_referrer_t *referrers;
uintptr_t out_of_line_ness : 2;
uintptr_t num_refs : PTR_MINUS_2;
uintptr_t mask;
uintptr_t max_hash_displacement;
};
struct {
// out_of_line_ness field is low bits of inline_referrers[1]
weak_referrer_t inline_referrers[WEAK_INLINE_COUNT];
};
};
bool out_of_line() {
return (out_of_line_ness == REFERRERS_OUT_OF_LINE);
}
weak_entry_t& operator=(const weak_entry_t& other) {
memcpy(this, &other, sizeof(other));
return *this;
}
weak_entry_t(objc_object *newReferent, objc_object **newReferrer)
: referent(newReferent)
{
inline_referrers[0] = newReferrer;
for (int i = 1; i < WEAK_INLINE_COUNT; i++) {
inline_referrers[i] = nil;
}
}
};
- 存储的内容是
弱引用对象的指针地址 - 对象的弱引用个数 <=4 走静态存储(在weak_entry_t初始化的时候一并分配好), >4 走动态存储
总结
-
SideTables 可以理解为一个
全局的hash数组,里面存储了SideTable类型的数据,其长度为 8(或64) -
一个obj,对应了一个SideTable,但是一个SideTable,会对应多个obj。因为
SideTable的数量只有8(或64)个,所以会有很多obj共用同一个SideTable -
弱引用表中,key是
对象的地址,value是weak指针地址的数组(weak_entry_t) -
weak_unregister_no_lock 和 weak_register_no_lock 中都是对 weak_entry_t 类型的数组进行操作