oc中对象的内存对齐方式初探

为什么要内存对齐?

1、平台原因(移植原因)不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常。
2、性能原因:数据结构(尤其是栈)应该尽可能地在自然边界上对齐。原因在于,为了访问未对齐的内存,处理器需要作两次内存访问;而对齐的内存访问仅需要一次访问。

内存对齐规则

1:数据成员对⻬规则:结构(struct)(或联合体(union))的数据成员,第一个数据成员放在offset为0的地方,以后每个数据成员存储的起始位置要从该成员大小或者成员的子成员大小(只要该成员有子成员,比如说是数组,结构体等)的整数倍开始(比如int为4字节,则要从4的整数倍地址开始存储。

2.结构体作为成员:如果一个结构里有某些结构体成员,则结构体成员要从其内部最大元素大小的整数倍地址开始存储.(strust a里存有struct b ,b 里有char int double的元素,那b应该从8的整书倍开始存储)

3.收尾工作:结构体的总大小,也就是sizeof的结果,必须是其内部最大成员的整数倍,不足的要对齐

首先通过一个类的内存段来分析

image.png

x p打印当前对象内存段
iOS为小端模式,内存要倒着读
所以我们用一个命令自动帮我们整理好
x/4xg p 意思就是按照16进制,以4整段打印当前p对象
我们发现OC为我们做了一些优化,我们发现0x0000001200006261这个内存段存储了age,char1,char2,三个属性
我们po 0x00000012 打印18,也就是我们的age属性值
po 0x61 0x62 打印的分别为97 98 也就是我们a和b对应的ASCII码
这就是oc做的内存对齐优化,节省了内存开销

数据类型所占字节
C OC 32位 64位
bool BOOL(64位) 1 1
signed char (_ _signed char)int8_t、BOOL(32位) 1 1
unsigned char Boolean 1 1
short int16_t 2 2
unsigned short unichar 2 2
int int32_t、 NSInteger(32位)、boolean_t(32位) 4 4
unsigned int boolean_t(64位)、NSUInteger(32位) 4 4
long NSInteger(64位) 4 8
unsigned long NSUInteger(64位) 4 8
long long int64_t 8 8
float CGFloat(32位) 4 4
double CGFloat(64位) 8 8

demo

struct LGStruct1 {
    char a;     // 1 + 7          
    double b;   // 8
    int c;      // 4
    short d;    // 2 + 2
} MyStruct1;

struct LGStruct2 {
    double b;   // 8
    char a;     // 1 + 7
    int c;      // 4 
    short d;    // 2
} MyStruct2;
NSLog(@"%lu-%lu",sizeof(MyStruct1),sizeof(MyStruct2));

//打印结果为24-24

我们在来看一个demo

struct Struct1 {
    int a;              //4 + 4
    double b;           //8
    int c;              // 4
    char d;             //1
    short e;            //2
}myStruct1;

struct Struct2 {
    int a;                  //4 + 4
    double b;               //8
    char d;                 //1 + 7
    int c;                  //4
    short e;                //2
}myStruct2;
 NSLog(@"%lu - %lu",sizeof(myStruct1),sizeof(myStruct2));
//打印结果为24-32

只是属性顺序不一样,为什么最后得到的大小不一样呢
int 为4字节 , double为8字节, char为一字节,short为2字节
myStruct1中
a占[0-3]的位置
b占[4-15]
c占[16-19]
d占[20-21]
e占[22-23]
所以打印size为24
a为int类型,4字节,下个成员b为double占8字节,根据开头的对齐规则第一条,所以要+4

myStruct2中
char d + 7 是因为a+b= 16字节 则d为第17字节,c为4字节,offset要为4的倍数所以+7
则最后结果为32(最大成员的整数倍)

再来看一个面试经常遇到的坑

 LGTeacher  *p = [LGTeacher alloc];
        
        p.name = @"LG_Cooci";       //8
        p.age  = 18;                  //4 内存对齐 + 4
        p.height = 185;               //8
        p.hobby  = @"女";            //8
        NSLog(@"%@",p);
        // 5 * 8 == 40
        //malloc_size 为48
    //对象字节对齐是以16字节对齐.而属性为8字节对齐
        NSLog(@"%lu--------%lu",class_getInstanceSize([p class]),malloc_size((__bridge const void *)(p)));
打印结果为40 ---- ---- 48

这几个属性加起来也才32啊为什么是40 呢, 因为类还有一个isa指针,而isa指针占8位,所以打印为40

malloc为什么是48 呢,还要从源码开始
我嗯可以直接在libmalloc中调用calloc(1, 40),点进去calloc中可以看到

retval = malloc_zone_calloc(default_zone, num_items, size);

然后在点进去

ptr = zone->calloc(zone, num_items, size);

然后问题来了,在点击 calloc,死循环找不到下一步代码,我们可以看到->,这是一个属性函数,我们可以利用lldb打印

image.png

然后全局搜索default_zone_calloc

default_zone_calloc(malloc_zone_t *zone, size_t num_items, size_t size)
{
  zone = runtime_default_zone();
  
  return zone->calloc(zone, num_items, size);
}

同样我们打印一下zone->calloc

image.png

继续全局搜索nano_calloc

void *p = _nano_malloc_check_clear(nanozone, total_bytes, 1);

点击进去


image.png

点击segregated_size_to_fit进去
为了方便理解记忆,奉上一个流程分析图

calloc流程.jpg

static MALLOC_INLINE size_t
segregated_size_to_fit(nanozone_t *nanozone, size_t size, size_t *pKey)
{
    // size = 40
    size_t k, slot_bytes;

    if (0 == size) {
        size = NANO_REGIME_QUANTA_SIZE; // Historical behavior
    }
    // 40 + 16-1 >> 4 << 4
    // 40 - 16*3 = 48

    //
    // 16
    k = (size + NANO_REGIME_QUANTA_SIZE - 1) >> SHIFT_NANO_QUANTUM; // round up and shift for number of quanta
    slot_bytes = k << SHIFT_NANO_QUANTUM;                           // multiply by power of two quanta size
    *pKey = k - 1;                                                  // Zero-based!

    return slot_bytes;
}

一个内存对齐的算法,传进来的size经过算法计算之后必为16的倍数,40不为16的倍数,所以+ 8 = 48 为16的证书倍 +

算法原理同下
看一个简单一点的算法

#   define WORD_MASK 7UL
static inline uint32_t word_align(uint32_t x) {
    // 7+8 = 15
    // 0000 1111
    // 0000 1000
    //&
    // 1111 1000 ~7
    // 0000 1000 8
    
    // 0000 0111
    //
    // x + 7
    // 8
    // 8 二阶
    // (x + 7) >> 3 << 3
    return (x + WORD_MASK) & ~WORD_MASK;
}

比如我们传进来的是8
8 + 7 = 15
15转化为2进制
0000 1111

7转化为二进制
0000 0111
取非
1111 1000
&0000 1111
=0000 1000 转换为10进制为 8

综上所述,可以得出
1.对象申请的空间(40)和系统开辟的空间(48)是不一样的
2.对象字节对齐是16字节对齐,而属性是8字节对齐

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,546评论 6 507
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,224评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,911评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,737评论 1 294
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,753评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,598评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,338评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,249评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,696评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,888评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,013评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,731评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,348评论 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,929评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,048评论 1 270
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,203评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,960评论 2 355