前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >OC底层探索04-探索对象内存大小OC底层探索04-探索对象内存大小

OC底层探索04-探索对象内存大小OC底层探索04-探索对象内存大小

作者头像
用户8893176
发布2021-08-09 11:26:28
6830
发布2021-08-09 11:26:28
举报
文章被收录于专栏:小黑娃Henry

上篇中对对象的alloc方式OC底层探索03-常用的alloc,init,new到底做了什么?进行了简单探索。在alloc时使用了一个8/16字节对齐算法来计算内存大小,想没想过为什么要这样做呢?

举例对象内存大小

代码语言:javascript
复制
        HRTest * test = [HRTest alloc];
        test.name = @"Henry";  //8字节
        test.hobby = @"woman"; //8字节
        test.height = 180.0;   //8字节
        test.a = 'a';  //1字节
        test.ab = 'A'; //1字节
        NSLog(@"\n---test类型内存大小%lu\n---HRTest实际占用内存大小%lu\n----HRTest实际内存分配大小%lu",
              sizeof(test),
              class_getInstanceSize([test class]),
              malloc_size((__bridge const void*)(test)));
  • 直接计算:size = 8 * 3 + 1 * 2 = 26(猜想) 对象的属性大小计算是需要通过内存对齐来计算的,并不是简单的加法
  • log输出情况

出入还是非常大的,问题出在以下几点?

1. isa指针没有计算在内
代码语言:javascript
复制
/// 出自objc源码
typedef struct objc_class *Class;
struct objc_object {
    Class _Nonnull isa  OBJC_ISA_AVAILABILITY;
};

sizeof(x)是获取当前类型的内存大小,,上文中sizeof(test)的test是一个结构体的指针,也就得到一个指针内存占用8字节

所有的类在OC中最终都会编译为objc_object(在这个问题中可以看做父类),其中包含一个isa指针,所以需要再加上8字节

size = 8 + 8 * 3 + 1 * 2 = 34(猜想)

相比较打印结果中的实际内存占用还是有一些差距。

2. class_getInstanceSize

接下来就通过源码来看看class_getInstanceSize这个函数到底是如何计算一个类的内存大小的。

代码语言:javascript
复制
size_t class_getInstanceSize(Class cls)
{
    if (!cls) return 0;
    return cls->alignedInstanceSize();
}
uint32_t alignedInstanceSize() const {
        return word_align(unalignedInstanceSize());
}
// 8字节对齐
#   define WORD_MASK 7UL
static inline uint32_t word_align(uint32_t x) {
    return (x + WORD_MASK) & ~WORD_MASK;
}

根据源码发现在获取实际大小后,会进行8字节对齐,在下方有详细的计算过程。

size = class_getInstanceSize(34) => 40

  • 这就是一个对象实际内存占用的计算过程:iSA(指针)+ 属性大小 + 8字节对齐
3. HRTest实际内存分配大小却是48

居然这样就不得不去看看malloc_sizemalloc_size的源码是在libmalloc库里。可惜没找到对应的实现,换个角度从内存分配方法calloc(1, size)看起。

代码语言:javascript
复制
void * calloc(size_t num_items, size_t size)
{
    ...
    retval = malloc_zone_calloc(default_zone, num_items, size);
}
void * malloc_zone_calloc(...)
{
    ...
    //核心代码
    ptr = zone->calloc(zone, num_items, size);
}

这里对于zone->calloa()没法直接跟进,需要:

代码语言:javascript
复制
static void * default_zone_calloc(...)
{
    zone = runtime_default_zone();
    
    return zone->calloc(zone, num_items, size);
}
//使用相同的方式:
static void * nano_malloc(...)
{
    ...
    void *p = _nano_malloc_check_clear(nanozone, size, 0);
}
//很长但是我们的目的是找到内存大小计算的方法
static void * _nano_malloc_check_clear(...)
{
    void *ptr;
    size_t slot_key;
    size_t slot_bytes = segregated_size_to_fit(nanozone, size, &slot_key);
    ...
}
//终于找到了目标方法:内存计算规则
#define NANO_REGIME_QUANTA_SIZE (1 << SHIFT_NANO_QUANTUM)   // 16
static MALLOC_INLINE size_t
segregated_size_to_fit(...)
{
    ...
    k = (size + NANO_REGIME_QUANTA_SIZE - 1) >> SHIFT_NANO_QUANTUM;
    slot_bytes = k << SHIFT_NANO_QUANTUM;
    *pKey = k - 1;
    return slot_bytes;
}

在内存创建的时候系统是对实际内存占用进行了16字节对齐

40 按照16进制对齐 => 48

小结一下

当然在底层中对象的属性所占内存大小计算不简单的是做加法,而是使用了内存对齐的方法来进行计算,由于篇幅所限会在OC底层探索05-内存对齐

中对内存对齐做解释。

对象需要的实际内存需要 8字节对齐 ,而真实分配内存时又进行了16字节对齐。那么问题又来了为什么要这样做呢?

16字节对齐算法

本质就是通过位运算,将实际内存大小计算为16的倍数.8字节对齐也是类似的。

  1. 第一种方式: (x + size_t(15)) & ~size_t(15)
代码语言:javascript
复制
//拿31举例:
size(31) :            0001 1111
15 :                  0000 1111
size(31) + 15 :       0010 1110
~15          :        1111 0000
size(31) + 15 & ~15 : 0010 0000 -> 32
  1. 第二种方式: (x + size_t(15)) >>4 <<4
代码语言:javascript
复制
//拿31举例:
size(31) :            0001 1111
15 :                  0000 1111
size(31) + 15 :       0010 1110
   >> 4:              0000 0010
   << 4:              0010 0000 -> 32

字节对齐的优势:

  • CPU把内存当成是一块一块的,块的大小可以是2,4,8,16字节大小,因此CPU在读取内存时是一块一块进行读取的。每次内存存取都会产生一个固定的开销,减少内存存取次数将提升程序的性能。
  • 16字节对齐后,可以加快CPU读取速度,同时使访问更安全,不会产生访问混乱的情况

早期的iOS系统中对象内存大小计算是通过8字节对齐,在分配内存时又进行了16字节对齐;而现在iOS系统中对象的内存大小计算是直接进行16字节对齐,通过这种方式来进一步优化对象的创建流程。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2020/9/12 下,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 举例对象内存大小
    • 1. isa指针没有计算在内
      • 2. class_getInstanceSize
        • 3. HRTest实际内存分配大小却是48
          • 小结一下
          • 16字节对齐算法
          领券
          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档