OC中各种变量的存储内存地址
// 栈区
int i = 10;
int j = 10;
NSObject* obj = [NSObject new];
NSLog(@"%p", &i);
NSLog(@"%p", &j);
NSLog(@"%p", &obj);
2020-12-29 14:46:34.444711+0800 Performace001[42044:666158] 0x7ffee87abe0c
2020-12-29 14:46:34.445492+0800 Performace001[42044:666158] 0x7ffee87abe08
2020-12-29 14:46:34.445581+0800 Performace001[42044:666158] 0x7ffee87abe00
堆区
NSObject* obj1 = [NSObject new];
NSObject* obj2 = [NSObject new];
NSObject* obj3 = [NSObject new];
NSLog(@"%p", obj);
NSLog(@"%p", obj1);
NSLog(@"%p", obj2);
NSLog(@"%p", obj3);
2020-12-29 14:46:34.445638+0800 Performace001[42044:666158] 0x600002cf40e0
2020-12-29 14:46:34.445702+0800 Performace001[42044:666158] 0x600002cf0000
2020-12-29 14:46:34.445755+0800 Performace001[42044:666158] 0x600002cf0010
2020-12-29 14:46:34.445831+0800 Performace001[42044:666158] 0x600002cf0020
系统针对小对象做的内存优化,Tagged Pointer
Tagged Pointer专门用来存储晓得对象,例如:NSNumber和NSDate.
Tagged Pointer指针的值不再是地址,而是真正的值。所以他不是一个对象,是一个披着对象皮的普通变量而已。所以它不存储在堆,也不需要alloc和释放。
直接在栈读取,速度快。
for (int i = 0; i < 10; i++) {
NSNumber* num = @(i*1.0f);
NSLog(@"%p", num);
}
2020-12-29 14:35:43.755619+0800 Performance002[41600:649989] 0xb34e05bf934add54
2020-12-29 14:35:43.756080+0800 Performance002[41600:649989] 0xb34e05bf934add44
2020-12-29 14:35:43.756166+0800 Performance002[41600:649989] 0xb34e05bf934add74
2020-12-29 14:35:43.756236+0800 Performance002[41600:649989] 0xb34e05bf934add64
2020-12-29 14:35:43.756315+0800 Performance002[41600:649989] 0xb34e05bf934add14
2020-12-29 14:35:43.756376+0800 Performance002[41600:649989] 0xb34e05bf934add04
2020-12-29 14:35:43.756428+0800 Performance002[41600:649989] 0xb34e05bf934add34
2020-12-29 14:35:43.756472+0800 Performance002[41600:649989] 0xb34e05bf934add24
2020-12-29 14:35:43.756535+0800 Performance002[41600:649989] 0xb34e05bf934addd4
2020-12-29 14:35:43.756633+0800 Performance002[41600:649989] 0xb34e05bf934addc4
大对象例子:
for (int i = 0; i < 10; i++) {
NSNumber* num = @(i*0xFFFFFFFFFFFFFFF);
NSLog(@"%p", num);
}
2020-12-29 14:38:05.773636+0800 Performance002[41665:651691] 0xa45d2595b2beb8b5
2020-12-29 14:38:05.774045+0800 Performance002[41665:651691] 0x60000284dc60
2020-12-29 14:38:05.774148+0800 Performance002[41665:651691] 0x600002864000
2020-12-29 14:38:05.774242+0800 Performance002[41665:651691] 0x600002848080
2020-12-29 14:38:05.774306+0800 Performance002[41665:651691] 0x6000028681e0
2020-12-29 14:38:05.774367+0800 Performance002[41665:651691] 0x60000286dc80
2020-12-29 14:38:05.774463+0800 Performance002[41665:651691] 0x600002860000
2020-12-29 14:38:05.774523+0800 Performance002[41665:651691] 0x600002864000
2020-12-29 14:38:05.774585+0800 Performance002[41665:651691] 0x600002848080
2020-12-29 14:38:05.774644+0800 Performance002[41665:651691] 0x600002860000
0x6开头是在堆区了
再来个:
NSString* s = @"123";
NSString* s1 = [NSString stringWithFormat:@"123"];
NSString* s2 = [NSString stringWithFormat:@"12344556679"];
NSLog(@"s = %p \n s1 = %p \n s2 = %p \n", s, s1, s2);
2020-12-29 14:40:39.794167+0800 Performance002[41734:653838] s = 0x10d178088
s1 = 0xcadc0920647f9d1f
s2 = 0x600003b300c0
系统判断:s的地址是在常量区,s1是一个值存在栈区,s2是堆区
引用计数
isa_t
isa_t是一个联合体,uintptr_t bits有64位
union isa_t
{
isa_t() { }
isa_t(uintptr_t value) : bits(value) { }
Class cls;
uintptr_t bits;
#if SUPPORT_PACKED_ISA
// extra_rc must be the MSB-most field (so it matches carry/overflow flags)
// nonpointer must be the LSB (fixme or get rid of it)
// shiftcls must occupy the same bits that a real class pointer would
// bits + RC_ONE is equivalent to extra_rc + 1
// RC_HALF is the high bit of extra_rc (i.e. half of its range)
// future expansion:
// uintptr_t fast_rr : 1; // no r/r overrides
// uintptr_t lock : 2; // lock for atomic property, @synch
// uintptr_t extraBytes : 1; // allocated with extra bytes
# if __arm64__
# define ISA_MASK 0x0000000ffffffff8ULL
# define ISA_MAGIC_MASK 0x000003f000000001ULL
# define ISA_MAGIC_VALUE 0x000001a000000001ULL
// 位域
struct {
uintptr_t nonpointer : 1; // 数字代表占多少位
uintptr_t has_assoc : 1;
uintptr_t has_cxx_dtor : 1;
uintptr_t shiftcls : 33; // MACH_VM_MAX_ADDRESS 0x1000000000
uintptr_t magic : 6;
uintptr_t weakly_referenced : 1;
uintptr_t deallocating : 1;
uintptr_t has_sidetable_rc : 1;
uintptr_t extra_rc : 19;
# define RC_ONE (1ULL<<45)
# define RC_HALF (1ULL<<18)
};
# elif __x86_64__
# define ISA_MASK 0x00007ffffffffff8ULL
# define ISA_MAGIC_MASK 0x001f800000000001ULL
# define ISA_MAGIC_VALUE 0x001d800000000001ULL
struct {
uintptr_t nonpointer : 1;
uintptr_t has_assoc : 1;
uintptr_t has_cxx_dtor : 1;
uintptr_t shiftcls : 44; // MACH_VM_MAX_ADDRESS 0x7fffffe00000
uintptr_t magic : 6;
uintptr_t weakly_referenced : 1;
uintptr_t deallocating : 1;
uintptr_t has_sidetable_rc : 1;
uintptr_t extra_rc : 8;
# define RC_ONE (1ULL<<56)
# define RC_HALF (1ULL<<7)
};
# else
# error unknown architecture for packed isa
# endif
// SUPPORT_PACKED_ISA
#endif
#if SUPPORT_INDEXED_ISA
# if __ARM_ARCH_7K__ >= 2
# define ISA_INDEX_IS_NPI 1
# define ISA_INDEX_MASK 0x0001FFFC
# define ISA_INDEX_SHIFT 2
# define ISA_INDEX_BITS 15
# define ISA_INDEX_COUNT (1 << ISA_INDEX_BITS)
# define ISA_INDEX_MAGIC_MASK 0x001E0001
# define ISA_INDEX_MAGIC_VALUE 0x001C0001
struct {
uintptr_t nonpointer : 1;
uintptr_t has_assoc : 1;
uintptr_t indexcls : 15;
uintptr_t magic : 4;
uintptr_t has_cxx_dtor : 1;
uintptr_t weakly_referenced : 1;
uintptr_t deallocating : 1;
uintptr_t has_sidetable_rc : 1;
uintptr_t extra_rc : 7;
# define RC_ONE (1ULL<<25)
# define RC_HALF (1ULL<<6)
};
# else
# error unknown architecture for indexed isa
# endif
// SUPPORT_INDEXED_ISA
#endif
};
arm64是手机的,struct刚好有64位,对应分别存储一些东西。
cls 变量会指向对象所属的类的结构,在 64 位设备上会占用 8byte。8字节64位,每一位都对应值的作用,可以省内存。
这部分是存储引用计数的位
uintptr_t has_sidetable_rc : 1;
uintptr_t extra_rc : 19;
retaincount函数
inline uintptr_t
objc_object::rootRetainCount()
{
if (isTaggedPointer()) return (uintptr_t)this;
sidetable_lock();
isa_t bits = LoadExclusive(&isa.bits);
ClearExclusive(&isa.bits);
if (bits.nonpointer) {
uintptr_t rc = 1 + bits.extra_rc;
if (bits.has_sidetable_rc) {
rc += sidetable_getExtraRC_nolock();
}
sidetable_unlock();
return rc;
}
sidetable_unlock();
return sidetable_retainCount();
}
如果 if (isTaggedPointer()) return (uintptr_t)this;,就不使用引用计数,直接返回。
如果 if (bits.has_sidetable_rc)散列表有值,就加上。
散列表,SideTable,结构体
struct SideTable {
spinlock_t slock;
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
static void lockTwo(SideTable *lock1, SideTable *lock2);
template
static void unlockTwo(SideTable *lock1, SideTable *lock2);
};
Object的retain
ALWAYS_INLINE id
objc_object::rootRetain(bool tryRetain, bool handleOverflow)
{
if (isTaggedPointer()) return (id)this;
bool sideTableLocked = false;
bool transcribeToSideTable = false;
isa_t oldisa;
isa_t newisa;
do {
transcribeToSideTable = false;
oldisa = LoadExclusive(&isa.bits);
newisa = oldisa;
if (slowpath(!newisa.nonpointer)) {
ClearExclusive(&isa.bits);
if (!tryRetain && sideTableLocked) sidetable_unlock();
if (tryRetain) return sidetable_tryRetain() ? (id)this : nil;
else return sidetable_retain();
}
// don't check newisa.fast_rr; we already called any RR overrides
if (slowpath(tryRetain && newisa.deallocating)) {
ClearExclusive(&isa.bits);
if (!tryRetain && sideTableLocked) sidetable_unlock();
return nil;
}
uintptr_t carry;
newisa.bits = addc(newisa.bits, RC_ONE, 0, &carry); // extra_rc++
if (slowpath(carry)) {
// newisa.extra_rc++ overflowed
if (!handleOverflow) {
ClearExclusive(&isa.bits);
return rootRetain_overflow(tryRetain);
}
// Leave half of the retain counts inline and
// prepare to copy the other half to the side table.
if (!tryRetain && !sideTableLocked) sidetable_lock();
sideTableLocked = true;
transcribeToSideTable = true;
newisa.extra_rc = RC_HALF;
newisa.has_sidetable_rc = true;
}
} while (slowpath(!StoreExclusive(&isa.bits, oldisa.bits, newisa.bits)));
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();
return (id)this;
}
引用计数的++:newisa.bits = addc(newisa.bits, RC_ONE, 0, &carry); // extra_rc++
这个时候可能溢出的情况,>2^19
// Leave half of the retain counts inline and
// prepare to copy the other half to the side table.
if (!tryRetain && !sideTableLocked) sidetable_lock();
sideTableLocked = true;
transcribeToSideTable = true;
newisa.extra_rc = RC_HALF;
newisa.has_sidetable_rc = true;
newisa.extra_rc = RC_HALF;这句是把extra_rc取一半2^18,然后另一半拷贝入散列表sideTable
if (slowpath(transcribeToSideTable)) {
// Copy the other half of the retain counts to the side table.
sidetable_addExtraRC_nolock(RC_HALF);
}
release
extra_rc-- 可能发生下溢出,就会去散列表中取回来
总结:weak实现原理
在SideTable中,weak_table_t weak_table;是一个散列表。
弱引用对象,底层也是使用散列表存储,对象的内存地址作为key,指向该对象的所有弱引用的指针作为值。
释放时
调用cleardeallocating函数,找到SideTable,通过isa.weakly_referenced判断是否有弱引用表,weak_table是一个弱引用散列表,以对象referent_id的内存地址作为key,去存储弱引用对象的哈希表中entry,遍历entry指向的referrers找到所有的弱引用对象,置为nil,最后移除弱引用散列表。