内存布局
- 内核区 Oxc0000000
- 栈区 stack 向下
- 常规内存512KB 8个字节 65536个指针变量
- 堆区 heap 向上
- 未初始化数据 .bss
- 已初始化数据 .data
- 代码段 .text
- 保留
- (void)memories {
int a = 10;
NSLog(@"%p",&a); // 栈 -- 0x7 栈
static int x = 4;
NSLog(@"%p",&x); // 静态/敞亮 -- 0x1 开头
NSArray *b = @[@"my", @"name", @"is"];
NSLog(@"b: %@-%p", b, &b); // 0x6 堆 对象在堆 指针在栈
NSLog(@"b[0]: %@-%p", b[0], b[0]); // b[0]是常亮0x1开头
NSObject *obj = [NSObject new]; // 对象 --
NSLog(@"%@ - %p", obj, &obj); // 0x6 堆 对象在堆 指针在栈
NSArray *array = [NSArray arrayWithObject:@1];
NSLog(@"%@-%p", array, &array);
}
运行结果:
2020-05-29 14:51:10.949423+0800 TheMemoryStudy[33080:4559849] 0x7ffee151f0cc
2020-05-29 14:51:10.949592+0800 TheMemoryStudy[33080:4559849] 0x10e6e24f8
2020-05-29 14:51:10.949727+0800 TheMemoryStudy[33080:4559849] b: (
my,
name,
is
)-0x7ffee151f0c0
2020-05-29 14:51:10.949842+0800 TheMemoryStudy[33080:4559849] b[0]: my-0x10e6e0040
2020-05-29 14:51:10.949979+0800 TheMemoryStudy[33080:4559849] <NSObject: 0x60000386c6e0> - 0x7ffee151f0b8
(lldb) po array
<__NSSingleObjectArrayI 0x600003860960>(
1
)
2020-05-29 14:51:37.853619+0800 TheMemoryStudy[33080:4559849] (
1
)-0x7ffee151f0b0
- 静态变量
// 一般是放在.m里的, 编译时候放在已初始化数据里.
// 如果放在.h里的话 在其他地方改变值时候,会把personNum拷贝到内存一份然后修正其值
static int personNum = 10;
- 堆栈溢出,表示堆区和栈区碰撞了.
- 尽量封装方法,函数. 加快编译速度. 因为一个方法越长,更加难被识别,所以编译速度更慢. -- 用空间换时间.
内存管理方案
- 先说异或操作 ^ // 相同为0 不同为1
a = 1010 0001
b = 0000 1101
a = a^b => 1010 1100
b = a^b => 1010 0001
a = a^b => 0000 1101
最后a和b互换了.
a = a^b^b
- TaggedPointer源码
static inline void * _Nonnull
_objc_makeTaggedPointer(objc_tag_index_t tag, uintptr_t value)
{
// PAYLOAD_LSHIFT and PAYLOAD_RSHIFT are the payload extraction shifts.
// They are reversed here for payload insertion.
// ASSERT(_objc_taggedPointersEnabled());
if (tag <= OBJC_TAG_Last60BitPayload) {
// ASSERT(((value << _OBJC_TAG_PAYLOAD_RSHIFT) >> _OBJC_TAG_PAYLOAD_LSHIFT) == value);
uintptr_t result =
(_OBJC_TAG_MASK |
((uintptr_t)tag << _OBJC_TAG_INDEX_SHIFT) |
((value << _OBJC_TAG_PAYLOAD_RSHIFT) >> _OBJC_TAG_PAYLOAD_LSHIFT));
return _objc_encodeTaggedPointer(result);
} else {
// ASSERT(tag >= OBJC_TAG_First52BitPayload);
// ASSERT(tag <= OBJC_TAG_Last52BitPayload);
// ASSERT(((value << _OBJC_TAG_EXT_PAYLOAD_RSHIFT) >> _OBJC_TAG_EXT_PAYLOAD_LSHIFT) == value);
uintptr_t result =
(_OBJC_TAG_EXT_MASK |
((uintptr_t)(tag - OBJC_TAG_First52BitPayload) << _OBJC_TAG_EXT_INDEX_SHIFT) |
((value << _OBJC_TAG_EXT_PAYLOAD_RSHIFT) >> _OBJC_TAG_EXT_PAYLOAD_LSHIFT));
return _objc_encodeTaggedPointer(result);
}
}
static inline void * _Nonnull
_objc_encodeTaggedPointer(uintptr_t ptr)
{
return (void *)(objc_debug_taggedpointer_obfuscator ^ ptr);
}
static inline uintptr_t
_objc_decodeTaggedPointer(const void * _Nullable ptr)
{
return (uintptr_t)ptr ^ objc_debug_taggedpointer_obfuscator;
}
makeTaggedPointer方法最后,return的_objc_encodeTaggedPointer就是一个异或操作后的值. 取值时候再异或一次就出来原来的值了.
那么TaggedPointer到底是什么呢? TaggedPointer是用来存储小对象用的一种数据结构.
Tagged pointers allow certain classes with small amounts of per-instance data to be stored entirely within the pointer. This can eliminate the need for memory allocations for many uses of classes like NSNumber, and can make for a good performance boost.
简单来说
NSObject *obj = [NSObject new];
地址->值
而TaggedPointer里不光有地址,还存了真正的值. 地址+值
所以taggedPointer创建和读取的数据非常快.
一般来说 NSNumber, 短的NSString等等... 而且taggedPointer是不经过retain和release的.
__attribute__((aligned(16), flatten, noinline))
id
objc_retain(id obj)
{
if (!obj) return obj;
if (obj->isTaggedPointer()) return obj;
return obj->retain();
}
__attribute__((aligned(16), flatten, noinline))
void
objc_release(id obj)
{
if (!obj) return;
if (obj->isTaggedPointer()) return;
return obj->release();
}
散列表(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<HaveOld, HaveNew>
static void lockTwo(SideTable *lock1, SideTable *lock2);
template<HaveOld, HaveNew>
static void unlockTwo(SideTable *lock1, SideTable *lock2);
};
-
结构:
- SideTable里有:
- Spinlock_t 锁
- RefcountMap 引用计数表
- weak_table_t 弱引用表
- SideTables里有很多个sideTable(哈希表里存了很多个哈希表)
- 为什么不用一张SideTable呢?
- 因为Sidetable里是有引用计数的操作的. 在这一过程中会有锁的操作(散列表里的Spinlock_t). 如果只用一张表的话频繁操作对性能就有影响了. 而使用好多个Sidetable,可以将锁分离.
- SideTable里有:
-
SideTable的读取:
- 先通过数组SideTables[obj], key是obj获取value. 也就是在SideTables里获取SideTable
-
弱引用表weak_table_t
这里引用一下Cooci老师的图
- 弱引用源码:
id
objc_initWeak(id *location, id newObj)
{
if (!newObj) {
*location = nil;
return nil;
}
return storeWeak<DontHaveOld, DoHaveNew, DoCrashIfDeallocating>
(location, (objc_object*)newObj);
}
static id
storeWeak(id *location, objc_object *newObj)
{
// ... 省略
// Assign new value, if any.
if (haveNew) {
newObj = (objc_object *)
weak_register_no_lock(&newTable->weak_table, (id)newObj, location,
crashIfDeallocating);
// weak_register_no_lock returns nil if weak store should be rejected
// Set is-weakly-referenced bit in refcount table.
if (newObj && !newObj->isTaggedPointer()) {
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);
return (id)newObj;
}
id
weak_register_no_lock(weak_table_t *weak_table, id referent_id,
id *referrer_id, bool crashIfDeallocating)
{
// ... 省略
weak_entry_t *entry;
if ((entry = weak_entry_for_referent(weak_table, referent))) {
append_referrer(entry, referrer);
}
else {
weak_entry_t new_entry(referent, referrer);
weak_grow_maybe(weak_table);
weak_entry_insert(weak_table, &new_entry);
}
// Do not set *referrer. objc_storeWeak() requires that the
// value not change.
return referent_id;
}
static weak_entry_t *
weak_entry_for_referent(weak_table_t *weak_table, objc_object *referent)
{
ASSERT(referent);
weak_entry_t *weak_entries = weak_table->weak_entries;
if (!weak_entries) return nil;
size_t begin = hash_pointer(referent) & weak_table->mask;
size_t index = begin;
size_t hash_displacement = 0;
while (weak_table->weak_entries[index].referent != referent) {
index = (index+1) & weak_table->mask;
if (index == begin) bad_weak_table(weak_table->weak_entries);
hash_displacement++;
if (hash_displacement > weak_table->max_hash_displacement) {
return nil;
}
}
return &weak_table->weak_entries[index];
}
解析:
Sidetables获取sidetable
sidetable获取weakTable
// weak_register_no_lock方法里
weakTable获取weak_entries
weak_entries获取entry // weak_entry_for_referent 拿entry
拿到了就给entry {
referrers,
num_refs,
inline_referrers
} 赋值 //
没位置就扩容(到 3/4 就扩容) // weak_grow_maybe
然后加进去 // weak_entry_insert
- dealloc 源码
id
object_dispose(id obj)
{
if (!obj) return nil;
objc_destructInstance(obj);
free(obj);
return nil;
}
void *objc_destructInstance(id obj)
{
if (obj) {
// Read all of the flags at once for performance.
bool cxx = obj->hasCxxDtor();
bool assoc = obj->hasAssociatedObjects();
// This order is important.
if (cxx) object_cxxDestruct(obj);
if (assoc) _object_remove_assocations(obj);
obj->clearDeallocating();
}
return obj;
}
从源码中看出dealloc里做的操作:
* 有析构函数,执行析构函数
* 有associated对象, 清理关联对象
* clearDeallocating清理弱引用对象
* referrer = nil
* weak_entry_remove(weak_table, entry)
* table.refcnts.erase(this)
- strong
void
objc_storeStrong(id *location, id obj)
{
id prev = *location;
if (obj == prev) {
return;
}
objc_retain(obj);
*location = obj;
objc_release(prev);
}
这里没什么可说的, 持有新的obj,释放旧的obj
总结
- 内存布局从高到底一次是内核,栈,堆,数据,代码.
- NSNumber,短的NSString等对象是TaggedPointer的,存的是地址+值.为了快速和节省内存.
- taggedPointer不做引用计数的加减.
- 引用计数是分两个地方,存储在extra_rc和sidetable里的.
- 弱引用的添加. 类似于@synthesizer锁里的SyncData这种拉链表.找到对应位置,加进去,到3/4就扩容然后加进去.
- dealloc方法做的事情:有析构函数,执行析构函数.有关联对象,处理关联对象.清理弱引用对象
- 强引用里做的事情: 持有新对象.赋值.释放旧对象.