前言
本文来探讨, 结构体对齐, 位域, 联合体对齐, 系统内存开辟, 内存对齐原则.
上一篇的坑
上一篇中留了一个大坑, 就是在汇编的时候objc_alloc这个符号其实在源码中是没有调用的. 由于_objc_rootAlloc和这个乍一看没在意, 感觉就是一个东西, 所以忽略了. 后面多参考参考其他人的方式专门写一篇, 遇到类似这种问题无从下手的问题, 怎么入手的解决的办法和逻辑.
因为这种问题的入手思维, 尝试不同的解决方式, 我觉得是非常重要的. 所以打算另起一篇
结构体对齐
先讨论没有自定义pack的情况, 自定义pack参数只能是 '1', '2', '4', '8', or '16'就是按照指定的pack参数进行对齐, 搞懂了没有pack的情况 ,有pack的自然懂。
结构体
结构体对齐的原则:
在没有定义 #pragma pack(value)的情况下, 原则如下:
- 第一个成员的首地址为0.
- 每个成员的首地址是自身大小的整数倍
- 结构体的总大小,为其成员中所含最大类型的整数倍。
如上图所示, 每一个成员的起始地址都是自身大小的整数倍, 结构体的总大小是成员变量中最大的成员的整数倍.
结构体嵌套结构体
先上图:
联合体 && 结构体嵌套联合体
位域 && 结构体嵌套位域
- 位域(位段)结构的大小既取决于结构内部所有位域的总大小,也取决于该位域声明时的类型
- 当一个位域结构内部所有位域总大小小于该结构中位域声明时的类型中长度最大的类型长度
时,该结构大小为该类型长度 (此处用用大小和长度区分自定义结构和C语言内置类型)- 位域结构的大小总为该结构中存在的所有的位域在声明时所用的内置类型中长度最大的内置类型的长度的整数倍
继续上图:
结构体嵌套位域
总体来说, 位域联合体这种单片机什么的用的多, 正常了解一下即可. 知道怎么计算, 对内存有一个好的认识就行了.
内存对齐原则
上面的东西看懂了, OC的原则也是一样, 有内存优化, 不完全按照顺序排列, 对齐方式都是一致. 先看一下自定义类和原生的size区别:
上面两个图可以知道, 成员变量对一个类的实际大小有影响, 经过测试, 方法, 属性都没有影响. 但是@property会自动合成set, get, ivar所以属性也可以实际影响到内存大小. 有兴趣的可以@synthesize @dynamic, 里面还是有坑点的.
图9中对, age, c1, c2进行了优化, 都放在了同一个8字节中处理, 测试在加入两个char也会一同合并进去, 都是来自于OC对内存的优化.
系统内存开辟
先看下面的代码:
可以自己猜测一下结果先
sizeof
准确点说的话,sizeof并不是一个函数,当做是一个操作符,主要作用于编译时,作用的对象是数据类型。
sizeof 只会计算类型所占用的内存大小,不会关心具体的对象的内存布局;
sizeof(p), p是一个指向堆区Person空间的指针, 所以p的大小为8.
class_getInstanceSize
class_getInstanceSize为对象实际开辟的内存空间, 应该都知道是8字节对齐, 也说不上来为什么, 跟着objc的源码看一下, 还是通过汇编慢慢往里找也可以. 上一篇已经说过方案了, 查找流程都一致. 直接看objc:
//不多bb, 直接全局搜索, 直接找到, 往里面进
size_t class_getInstanceSize(Class cls)
{
if (!cls) return 0;
return cls->alignedInstanceSize();
}
uint32_t alignedInstanceSize() const {
return word_align(unalignedInstanceSize());
}
#ifdef __LP64__
# define WORD_SHIFT 3UL
# define WORD_MASK 7UL
# define WORD_BITS 64
#else
# define WORD_SHIFT 2UL
# define WORD_MASK 3UL
# define WORD_BITS 32
#endif
//核心就在这里, 上一篇也讲到过, 在32位中就是4字节对齐, 64位中就是8字节对齐,
//一个宏定义兼容了两种系统, 并且代码也不用改动, 多好.
static inline size_t word_align(size_t x) {
return (x + WORD_MASK) & ~WORD_MASK;
}
malloc_size
实际调用的malloc_size, 点击进入查看:
进入objc源码查看也没有, 完全无从下手, 此时可以留意左上角的红线.
此时: 进入思考, 怎么找到目标实现, 是否开源, 一无所知.
直接谷歌oc中malloc源码在哪个文档里, 出来一堆, 相信你看得懂
- 直接赋值粘贴方法谷歌搜索
可以看到apple 开源官方文档里面有, 假设我们什么都搜不到, 什么都不知道, 只能去stack overflow找寻一下, 如果malloc的任何信息都搜索不到, 那么只能靠自己钻研了, 尝试像别人请教请教也自己钻研一下.
继续进入libmalloc的库, github上有很多提供可编译的版本拉过来直接用.
从上面的图可知有malloc.h, 直接malloc的实现搜索malloc.c:
直接找到malloc.c了, 进入探索一下.
- 汇编
2, 源码
从上面汇编可以看出, 是一直在调用 看一下find_registered_zone内部, 但是经过调试断点其实return的地方都断不到, 此时只能跟着汇编走流程了, 一步一步, 大概断言到了几个流程, 但是由于我对x86的指令集不熟悉, 电脑usb口坏了, 只能大致记录一下过程, 等电脑修好大致在走一遍.
//最后走入了malloc.c 652行 最后等同于malloc_zones[0];
1. lite_zone->malloc_zones->nano_size->_nano_vet_and_size_of_live
->_nano_block_inuse_p
'''
这些代码的注释大多都是虚拟地址分配的, 我看的大致的意思就是注册过的内存malloc_zones, 当前进来的替换掉0位置的数据, 然后把0中的size返回回去. 也有可能是我理解错误, 后面电脑修好了 我在验证一下.
总结
知识太多, 有效吸收理解, 并不需要浪费大量时间研究并不是很有意义的东西, 对我们普通开发者来说, 比如find_registered_zone这段代码, 大致的注释就是虚拟内存分配的一些东西, 需要搞懂虚拟内存分配的流程, 像这种的有兴趣可以直接去读深入理解计算机原理, 理解之后估计能找到那种互通的感觉.
跟上时代的步伐, 一步一步慢慢走..稳住我们能赢!!!