本篇文章通过一个小例子过一下malloc的源码
首先我们建立一个person类,对应建立几个属性,然后执行对sizeof(),class_getInstanceSize(),malloc_size()的打印。
Person *person = [Person alloc];
person.name = @"ErBao";
person.nickName = @"EB";
NSLog(@"sizeof(persion): %lu", sizeof(person));
NSLog(@"class_getInstanceSize([Person class]): %lu", class_getInstanceSize([Person class]));
NSLog(@"malloc_size((__bridge const void *)(person)): %lu", malloc_size((__bridge const void *)(person)));
打印出来的结果是:
- sizeof(persion): 8
- class_getInstanceSize([LGPerson class]): 40
- malloc_size((__bridge const void *)(person)): 48
解析:
- sizeof(person)中的person是指针, 一个指针占8个字节, 所以sizeof(person) 为8
- class_getInstanceSize 采用8字节对齐,类实例的字节大小,各属性所占内存大小如下图
8+8+4+8=28,加上isa指针的8字节后为36, 按内存对齐原则,总大小须是最长属性大小整数倍,所以结果为40下面我们分析一下,为什么malloc_size((__bridge const void *)(person))等于48 malloc的源码位于libMalloc库中,我们先下载好源码opensource.apple.com/tarballs/li… 首先用calloc在内存的动态存储区中分配1块长度为40字节的连续区域void *p = calloc(1, 40);点击calloc 调用如下源码:
然后我们进入到_malloc_zone_calloc这个方法.
这时候点击
calloc只有声明, 无法看到函数调用.这个时候我们在console里面执行po zone->calloc
然后我们根据输出结果找到实现的部分
可以看出在
default_zone_calloc 里面又执行了zone->calloc, 然后再来po一下.
找到
nano_calloc方法
_nano_malloc_check_clear推测可能开辟内存的大小 点进去查看(方法有点长,截图可能看的不清楚,我就直接粘贴代码段了):
**static** **void** *
_nano_malloc_check_clear(nanozone_t *nanozone, size_t size, boolean_t cleared_requested)
{
MALLOC_TRACE(TRACE_nano_malloc, (uintptr_t)nanozone, size, cleared_requested, 0);
void *ptr;
size_t slot_key;
size_t slot_bytes = segregated_size_to_fit(nanozone, size, &slot_key); // Note slot_key is set here
mag_index_t mag_index = nano_mag_index(nanozone);
nano_meta_admin_t pMeta = &(nanozone->meta_data[mag_index][slot_key]);
ptr = OSAtomicDequeue(&(pMeta->slot_LIFO), offsetof(**struct** chained_block_s, next));
if (ptr) {
unsigned debug_flags = nanozone->debug_flags;
#if NANO_FREE_DEQUEUE_DILIGENCE
size_t gotSize;
nano_blk_addr_t p; // the compiler holds this in a register
p.addr = (uint64_t)ptr; // Begin the dissection of ptr
if (NANOZONE_SIGNATURE != p.fields.nano_signature) {
malloc_zone_error(debug_flags, true, "Invalid signature for pointer %p dequeued from free list\n", ptr);
}
if (mag_index != p.fields.nano_mag_index) {
malloc_zone_error(debug_flags, true, "Mismatched magazine for pointer %p dequeued from free list\n", ptr);
}
gotSize = _nano_vet_and_size_of_free(nanozone, ptr);
if (0 == gotSize) {
malloc_zone_error(debug_flags, true, "Invalid pointer %p dequeued from free list\n", ptr);
}
if (gotSize != slot_bytes) {
malloc_zone_error(debug_flags, true, "Mismatched size for pointer %p dequeued from free list\n", ptr);
}
if (!_nano_block_has_canary_value(nanozone, ptr)) {
malloc_zone_error(debug_flags, true, "Heap corruption detected, free list canary is damaged for %p\n", "*** Incorrect guard value: %lu\n", ptr ((chained_block_t)ptr)->double_free_guard);
}
#if defined(DEBUG)
void *next = (void *)(((chained_block_t)ptr)->next);
if (next) {
p.addr = (uint64_t)next; // Begin the dissection of next
if (NANOZONE_SIGNATURE != p.fields.nano_signature) {
malloc_zone_error(debug_flags, true, "Invalid next signature for pointer %p dequeued from free " "list, next = %p\n", ptr, "next");
}
if (mag_index != p.fields.nano_mag_index) {
malloc_zone_error(debug_flags, true, "Mismatched next magazine for pointer %p dequeued from " "free list, next = %p\n", ptr, next);
}
gotSize = _nano_vet_and_size_of_free(nanozone, next);
if (0 == gotSize) {
malloc_zone_error(debug_flags, true, "Invalid next for pointer %p dequeued from free list", "next = %p\n", ptr, next);
}
if (gotSize != slot_bytes) {
malloc_zone_error(debug_flags, true, "Mismatched next size for pointer %p dequeued from free, "list, next = %p\n", ptr, next);
}
}
#endif /* DEBUG */
#endif /* NANO_FREE_DEQUEUE_DILIGENCE */
((chained_block_t)ptr)->double_free_guard = 0;
((chained_block_t)ptr)->next = **NULL**; // clear out next pointer to protect free list
} else {
ptr = segregated_next_block(nanozone, pMeta, slot_bytes, mag_index);
}
if (cleared_requested && ptr) {
memset(ptr, 0, slot_bytes); // **TODO: Needs a memory barrier after memset to ensure zeroes land first?**
}
return ptr;
}
其中ptr = OSAtomicDequeue(&(pMeta->slot_LIFO), offsetof(**struct** chained_block_s, next)); 我们看到了LIFO推测先进后出,所以ptr在栈区,将堆内存地址赋值给ptr.
segregated_next_block的底层是do while循环,在内存中查找可开辟的地址
#define SHIFT_NANO_QUANTUM 4.
在segregated_size_to_fit里先右移4位, 再左移4位.进行16字节对齐.所以上面malloc_size(person)为48
总结一下:
- 在
堆开辟的内存也就是对象的内存, 以16字节对齐 - 对象的
成员变量也就是结构体内部, 以8字节对齐 - 对象和对象之间在内存中是
16字节对齐 为什么对象中要以16字节对齐呢? 我们来简单的分析一下:
先说结论:
8字节发生错误的概率更大.
对象在内存中存储的示意图如下:
任何对象都至少包括一个8字节的
isa,然后加上还需要给成员变量分配空间,就算成员变量就占2个字节的空间,那么这个对象最后所占的空间为8(isa)+2(成员变量)+4(对齐) = 16字节. 一个对象至少都要占16个字节, 如果读取的时候以8个字节为一组来读取,那么计算的次数会增大, 发生错误的概率也会增大.
那为什么不用32位对齐呢?因为这样很多时候会导致空间的浪费.
附图
基本数据类型在内存中所占空间大小: