po 打印信息
p 打印详细的信息
bt 打出堆
register read 读取寄存器
x 读取内存段
x/4gx 读取4段内存段
LWPerson * p = [LWPerson alloc]; LWPerson * q; NSLog(@"对象类型占用内存大小--%lu",sizeof(p)); NSLog(@"对象类型占用内存大小--%lu",sizeof(q)); NSLog(@"对象实际内存大小-----%lu",class_getInstanceSize([p class])); NSLog(@"对象实际内存大小-----%lu",class_getInstanceSize([q class])); NSLog(@"系统为分配的内存大小--%lu",malloc_size((__bridge const void *)(p))); NSLog(@"系统为分配的内存大小--%lu",malloc_size((__bridge const void *)(q)));
2.2:打印结果如下
2020-09-19 22:59:42.507118+0800 KCObjcTest[4003:298596] 对象类型占用内存大小--8 2020-09-19 22:59:42.507678+0800 KCObjcTest[4003:298596] 对象类型占用内存大小--8 2020-09-19 22:59:42.507780+0800 KCObjcTest[4003:298596] 对象实际内存大小-----8 2020-09-19 22:59:42.507844+0800 KCObjcTest[4003:298596] 对象实际内存大小-----0 2020-09-19 22:59:42.507910+0800 KCObjcTest[4003:298596] 系统分配的内存大小--16 2020-09-19 22:59:42.507963+0800 KCObjcTest[4003:298596] 系统分配的内存大小--0
2.3:总结
sizeof
传进来的是类型,用来计算这个类型占多大内存,这个在编译器编译阶段就会确定,所以sizeof(p)
和sizeof(q)
的结果都是一样的,p
和q
都是指针类型,指针大小就是8个字节
。
class_getInstanceSize
对象的实际内存大小,大小由类的属性和变量来决定,实际上并不是严格意义上的对象内存大小,因为底层进行8字节对齐
算法define WORD_MASK 7UL
((x + WORD_MASK) & ~WORD_MASK
,LWPerson
类中没有其他的属性和变量,但是继承了NSObject
,NSObject
中有一个isa
指针,所以内存大小是8字节
malloc_size
系统分配的内存大小是按16字节对齐
的方式,即是按16的倍数
分配 ,不足则系统会自动填充字节(具体的calloc详细流程后续会更新
3:各类型所占字节
1、平台原因(移植原因):不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常。
2、性能原因:数据结构(尤其是栈)应该尽可能地在自然边界上对齐。原因在于,为了访问未对齐的内存,处理器需要作两次内存访问;而对齐的内存访问仅需要一次访问。
3、以空间换时间
参考: 为什么要进行内存对齐?
1、数据成员对?规则:结构(struct)(或联合(union))的数据成员,第一个数据成员放在offset为0的地方,以后每个数据成员存储的起始位置要从该成员大小或者成员的子成员大小(只要该成员有子成员,比如说是数组,结构体等)的整数倍开始(比如int为4字节,则要从4的整数倍地址开始存储)。
min(m, n)
的公式, 其中 m
表示当前成员的开始位置
, n
表示当前成员所需要的位数
。如果满足条件 m 整除 n
(即 m % n == 0
), n
从 m
位置开始存储, 反之继续检查 m+1 能否整除 n
, 直到可以整除, 从而就确定了当前成员的开始位置。2、结构体作为成员:如果一个结构里有某些结构体成员,则结构体成员要从其内部最大元素大小的整数倍地址开始存储.(struct a里存有struct b,b里有char,int ,double等元素,那b应该从8的整数倍开始存储.)
3、收尾工作:结构体的总大小,也就是sizeof的结果,必须是其内部最大成员的整数倍,不足的要补?
3.1:结构体MyStruct1 内存大小计算
根据内存对齐规则计算MyStruct1
的内存大小,详解过程如下:
变量a
:占1
个字节,从0开始,此时min(0,1)
,即 0 存储 a
变量b
:占8
个字节,从1开始,此时min(1,8)
,1不能整除8,继续往后移动,知道min(8,8)
,从8开始,即 8-15 存储 b
变量c
:占4
个字节,从16开始,此时min(16,4)
,16可以整除4,即 16-19 存储 c
变量d
:占2
个字节,从20开始,此时min(20, 2)
,20可以整除2,即20-21 存储 d
因此MyStruct1
的需要的内存大小为 15
字节,而MyStruct1中最大变量的字节数为8
,所以 MyStruct1 实际的内存大小必须是 8 的整数倍
,18向上取整到24,主要是因为24是8的整数倍,所以 sizeof(MyStruct1) 的结果是 24
3.2:结构体MyStruct2 内存大小计算
根据内存对齐规则计算MyStruct2
的内存大小,详解过程如下:
变量b
:占8
个字节,从0开始,此时min(0,8)
,即 0-7 存储 b
变量c
:占4
个字节,从8开始,此时min(8,4)
,8可以整除4,即 8-11 存储 c
变量d
:占2
个字节,从12开始,此时min(12, 2)
,12可以整除2,即12-13 存储 d
变量a
:占1
个字节,从14开始,此时min(14,1)
,即 14 存储 a
因此MyStruct2
的需要的内存大小为 15
字节,而MyStruct1中最大变量的字节数为8
,所以 MyStruct2 实际的内存大小必须是 8 的整数倍
,15向上取整到16,主要是因为16是8的整数倍,所以 sizeof(MyStruct2) 的结果是 16、
引用:单个结构体内存大小
结构体中嵌套结构体的内存大小计算情况
//1、结构体嵌套结构体 struct Mystruct3{ double b; //8字节 int c; //4字节 short d; //2字节 char a; //1字节 struct Mystruct2 str; }Mystruct3; //2、打印 Mystruct3 的内存大小 NSLog(@"Mystruct3内存大小:%lu", sizeof(Mystruct3)); NSLog(@"Mystruct3中结构体成员内存大小:%lu", sizeof(Mystruct3.str));
分析Mystruct3
的内存计算
根据内存对齐规则,来一步一步分析Mystruct3
内存大小的计算过程
变量b
:占8
个字节,从0开始,此时min(0,8)
,即 0-7 存储 b
变量c
:占4
个字节,从8开始,此时min(8,4)
,8可以整除4,即 8-11 存储 c
变量d
:占2
个字节,从12开始,此时min(12, 2)
,20可以整除2,即12-13 存储 d
变量a
:占1
个字节,从14开始,此时min(14,1)
,即 14 存储 a
结构体成员str
:str是一个结构体,根据内存对齐原则二
,结构体成员要从其内部最大成员大小的整数倍开始存储
,而MyStruct2
中最大的成员
大小为8
,所以str要从8的整数倍开始,当前是从15开始,所以不符合要求,需要往后移动到16,16是8的整数倍,符合内存对齐原则,所以 16-31 存储 str
因此MyStruct3
的需要的内存大小为 32
字节,而MyStruct3中最大变量为str, 其最大成员内存字节数为8
,根据内存对齐原则,所以 MyStruct3 实际的内存大小必须是 8 的整数倍
,32正好是8的整数倍,所以 sizeof(MyStruct3) 的结果是 32
MyStruct1 通过内存字节对齐原则,增加了9个字节,而MyStruct2通过内存字节对齐原则,通过4+2+1的组合,只需要补齐一个字节即可满足字节对齐规则,这里得出一个结论结构体内存大小与结构体成员内存大小的顺序有关
如果是结构体中数据成员
是根据内存从小到大
的顺序定义的,根据内存对齐规则来计算结构体内存大小,需要增加有较大的内存padding即内存占位符,才能满足内存对齐规则,比较浪费内存
如果是结构体中数据成员
是根据内存从大到小
的顺序定义的,根据内存对齐规则来计算结构体内存大小,我们只需要补齐少量内存padding
即可满足堆存对齐规则,这种方式就是苹果中采用的
,利用空间换时间,将类中的属性进行重排,来达到优化内存的目的
苹果中属性重排
,即内存优化
1:定义一个自定义CJLPerson类,并定义几个属性
@interface CJLPerson : NSObject @property (nonatomic, copy) NSString *name; @property (nonatomic, copy) NSString *nickName; // @property (nonatomic, copy) NSString *hobby; @property (nonatomic, assign) int age; @property (nonatomic, assign) long height; @property (nonatomic) char c1; @property (nonatomic) char c2; @end @implementation CJLPerson @end
2:在main中创建CJLPerson的实例对象,并对其中的几个属性赋值
int main(int argc, char * argv[]) { @autoreleasepool { CJLPerson *person = [CJLPerson alloc]; person.name = @"CJL"; person.nickName = @"C"; person.age = 18; person.c1 = ‘a‘; person.c2 = ‘b‘; NSLog(@"%@",person); } return 0; }
3:断点调试
当我们向通过0x0000001200006261
地址找出age等数据时,发现是乱码,这里无法找出值的原因是苹果中针对age、c1、c2属性的内存进行了重排
,因为age类型占4个字节,c1和c2类型char分别占1个字节,通过4+1+1
的方式,按照8字节对齐
,不足补齐的方式存储在同一块内存
中,
0x00000012
0x61
(a的ASCII码是97)0x62
(b的ASCII码是98)
4:CJLPerson的内存分布情况
注意:
1、char类型的数据读取出来是以ASCII码的形式显示
2、图片中地址为0x0000000000000000
,表示person中还有属性未赋值
这里可以总结下苹果中的内存对齐思想:
并不是所有的内存都可以进行浪费的
,苹果会自动对属性进行重排,以此来优化内存
原文:https://www.cnblogs.com/zyzmlc/p/14059114.html