iOS 内存对齐

1,243 阅读4分钟

不同类型数据所占字节 内存对齐

不同类型数据所占字节</>

1908026-1936d584e55c1ef8

内存对齐</>

在iOS 64位系统中,采用8字节对齐(计算属性内存空间大小总和),最小内存大小为16字节,实际分配空间是16字节对齐(实际分配空间)。

对象的属性内存布局遵循下面规则

  • 结构体变量的首地址是其最长基本类型成员的整数倍;
  • 结构体每个成员相对于结构体首地址的偏移量(offset)都是成员大小的整数倍,如不满足,对前一个成员填充字节以满足;
  • 结构体的总大小为结构体最大基本类型成员变量大小的整数倍;
  • 结构体作为成员:如果一个结构体内部成员包含其他结构体成员,则结构体成员要从其内部成员最大元素大小的整数倍地址开始存储
  • 结构体中的成员变量都是分配在连续的内存空间中。
  • 结构体成员顺序不同,会导致所占内存空间不一样;对象经过编译器优化,就不会有这个问题

输出结果

NSObject 的一个实例对象所占内存是多少

#import <objc/runtime.h>
#import <malloc/malloc.h>

// ChildrenObject 有 age, name 属性,
// 如果对象创建了没去赋值属性,它会是内存假地址
ChildrenObject *obj = [[ChildrenObject alloc] init];
obj.age = 20;

// class_getInstanceSize依赖于<objc/runtime.h>,返回创建一个实例对象所需内存大小。就是获取对象的全部属性的大小总和,然后按8位对齐获得,不足8位补齐8位。
NSLog(@"class_getInstanceSize = %zd", class_getInstanceSize([ChildrenObject class]));

// malloc_size依赖于<malloc/malloc.h>,返回系统分配给对象的内存大小,而且最小是16字节
NSLog(@"malloc_size = %zd", malloc_size((__bridge const void *)obj));

// 在64位架构下,自定义一个NSObject对象,无论该对象生命多少个成员变量,最后 sizeOf 得到的内存大小都是8个字节, 是因为 sizeOf获取的是类型所分配内存,所传参数为指针类型,所以最后得到的都是8
NSLog(@"sizeof = %zd", sizeof(obj));

注意上面三种方法所获取结果的不同

  • class_getInstance 获取实例对象在内存对齐的情况下,所占大小
  • malloc_size 获取的是实际系统所分配的内存大小
  • sizeOf 获取类型所占字节大小,如果传的是对象,永远都是8;如果是结构体实例获取的是

延伸

  • iOS 系统为对象实际分配内存空间为什么是16的倍数,有哪些策略

    libmalloc 分析 16的倍数保证了高效存取和合理的利用率,有 tiny, small, large 策略

  • 联合体,结构体,对象的异同

    • 结构体和类的区别
      • 结构体值类型,类是引用类型。结构体变量赋值是直接赋值,值传递;对象的赋值,是指针传递
      • 结构体没有继承特性,类有
      • 结构体有默认构造函数; class 需要自己构建构造函数,
      • 结构体只能封装属性,类可以封装属性和方法
      • 结构体变量分配在栈,OC对象分配堆;栈的空间小,访问速度快;堆空间大,访问速度慢
    • 结构体和联合体的区别
      • 结构体是
      • 联合体是什么
  • 结构体里有 对象成员(NSString, eg), 对象里有结构体属性,会有什么问题

  • 如果给类增加方法,类实例对象内存大小会变化么,为什么,为什么这么设计

    • 创建对象的时候,并不会给对象的方法分配内存,只会给属性、成员变量分配内存

    • 一个类可能创建多个实例,每个实例的方法都一样,没有差异性,完全可以抽离出来,所有对象共用这块存储方法的内存,实际上都存储在 类实例 里面了,一个类只有一个类实例,由系统创建。

    • 这么设计的好处就是节省空间,加快初始化速度等

资料

搜狐 NSObject 内存布局