Objet OC sous-jacent au développement et à l'implémentation de la mémoire (ci-dessous)

Dans la section précédente, nous avons exploré le processus de mise en œuvre d'alloc avec la couche inférieure du code source, et appris à identifier les différents états de la classe d'enregistrement, et résumé et expliqué les problèmes connexes Objcsoulevés . Problèmes d'alignement et de liaison de classe.LLVMalloc

1. Facteurs affectant la taille de la mémoire objet

Après l'exécution de alloc, le code appellera _objc_rootAllocWithZone-> _class_createInstanceFromZonepour ouvrir l'espace mémoire. Nous publions d'abord le code source 对象的内存大小et l'objet dans le système实际开辟的内存大小 ;

1. Lorsque la classe LGPerson n'a pas d'attributs et de méthodes

La taille de la mémoire dans le cas initial image.pngInitialement , la valeur par défaut 只有isa占用8个字节(la taille réelle de l'objet est alignée sur 16 octets, expliqué ci-dessous), donc la sortie 8, 8, 16;

2. Lorsque la classe LGPerson déclare quatre propriétés

image.png image.pngLes octets occupés par les attributs sont 1, 4, 8 et 8, et l'isa par défaut est 8. Selon le principe d'alignement de mémoire de structure (les connaissances de base doivent être familières), la sortie est 8, 32 et 32 ​​respectivement ;

Lorsque des attributs sont ajoutés, la taille des variables membres et la taille du développement du système sont augmentées

3. Lorsque l'attribut de classe LGPerson est remplacé par une variable membre

image.png image.pngC'est la même chose que la sortie de l'attribut, si vous modifiez l'ordre des variables membres

4. Échangez l'ordre des variables membres dans la classe LGPerson

image.png image.pngUne scène magique est apparue. Après avoir changé l'ordre des variables membres, la 成员变量的大小somme 系统开辟的大小a changé et la sortie est devenue 8, 40 et 48 ;

En effet, selon la structure 顺序决定了内存对齐, (alors que la taille réelle de l'objet est renvoyée dans un alignement de 16 octets).

5. La classe LGerson permute l'ordre des propriétés et ajoute deux méthodes

image.png image.pngChanger l'ordre des propriétés et ajouter des méthodes n'a aucun effet sur la taille de la mémoire

Nous pouvons déduire des 5 situations ci-dessus 影响对象内存大小的因素: 属性(或者成员变量)的个数et 成员变量的顺序; puis analysons comment le code source est implémenté.

Description de la fonction

  • sizeof 计算数据类型或者指针大小,p指针为8字节;
  • class_getInstanceSize类中成员变量的大小,在类结构体class_ro_t中instanceSize字段来记录(后面详细分析);
  • malloc_size计算实际向系统申请开辟的内存空间大小,遵循16字节对齐原则;

小结: 系统底层针对属性做了内存对齐的优化,无论属性顺序如何变化,都会按照最优的对齐方式对齐; 而成员变量的对齐并不会进行优化处理;

所以日常开发中我们尽量使用属性,不直接使用成员变量,节约内存;

二、_class_createInstanceFromZone源码分析

源码如下:

image.png 仔细分析上面源码,总结就是干了3件事:

  1. instanceSize计算成员变量的内存大小;
  2. calloc/malloc_zone_calloc 开辟内存空间大小(这个空间就是实例对象的空间大小)(默认执行calloc,因为参数默认传nil);
  3. initIsa/initInstanceIsa 关联isa和class;(默认执行initInstanceIsa,fast标识是否需要开启nonpointter,实例对象是isa_t联合体,类对象是isa指针) 接下来我们来看具体实现:

三、instanceSize内存计算

1、instanceSize方法

image.png 底层代码会读取cache(缓存)中的fastInstaceSize大小(存储在cache的_flags中,在上节中我们说过cache中的_flags第3-13位来存储instanceSize大小);

因为在编译阶段成员变量大小已经确定,存放在MachO文件中;当类加载时,会在realizeClassWithoutSwift->reconcileInstanceVariables计算父类的成员变量的大小,并修改class_ro_t中的起始大小(默认isa指针大小)和instanceSize大小;

在老版本的源代码中,是没有缓存的,在instanceSize方法中执行的是 读取缓存下面的代码。

size_t size = alignedInstanceSize() + extraBytes;

if (size < 16) size = 16;

return size; alignedInstanceSize()方法中word_align方法是8字节对齐算法(如上图标注部分);

2、cache.fastInstaceSize方法

image.png 读取_flags中缓存的成员变量的大小,【 在上节中我们说过cache中的_flags第3-12位来存储instanceSize大小,最小可存储8字节(1000),最大存储4M(1 0000 0000 0000, 1024*1024 *4字节)】,align16方法就是16字节对齐算法,所以对象的实际大小,以16字节对齐返回

下图 是类加载时如何缓存instanceSize大小:

realizeClassWithoutSwift方法中 image.png 加上父类的instanceSize大小后,调用setinstanceSize方法缓存记录;

reconcileInstanceVariables方法中: image.png 如果父类的成员变量大小 大于 起始的8个字节(所有对象都有默认的isa起始8个字节),则说明父类有其它的成员变量,调用moveIvars修改成员变量内存大小;

image.png 计算父类的成员变量大小,首先要减去起始默认的isa的字节(所有对象都有默认的isa起始8个字节);然后再8字节对齐;最后修改起始字节数和instanceSize大小;

小结:

在获取instanceSize大小时,成员变量大小(排序有关)都是以8字节对齐的;

而实际返回的对象大小是以16字节对齐的;

一个对象的占用内存大小 最小为8字节,最大为4M;

关于字节对齐word_align和align16算法???

四、calloc/malloc_zone_calloc内存开辟

id obj;

if (zone) {

   obj = (id)malloc_zone_calloc((malloc_zone_t *)zone, 1, size);

} else {

    obj = (id)calloc(1, size);

}

在开辟内存空间时,size大小就是返回对象的实际大小(以16字节对齐),我们通过跳转定义看到calloc/malloc_zone_calloc源码在malloc库中 image.png 我们在苹果的开发社区看到(malloc下载地址)

image.png 最新的源码版本为317.40.8,我们下载探究一下: image.png 在源码中calloc 其实就是调用的是malloc_zone_calloc方法,传入了一个默认的default_zone参数(初始化的一个malloc_zone_t结构体);

image.png 中间查找过程省略。。。 直接贴结果函数_nano_malloc_check_clear image.pngsegregated_size_to_fit对开辟的空间大小再次用16字节对齐验证;代码如下: image.png 宏定义NANO_REGIME_QUANTA_SIZE为16,SHIFT_NANO_QUANTUM为4,算法公式为(32+(16-1))>> 4 << 4, 此公式为16字节对齐计算公式。

五、initIsa/initInstanceIsa关联isa

分别看下initInstanceIsainitIsa底层代码 image.png image.png 可以看到底层都是调用了initIsa函数只不过参数不同而已;第一个参数为当前类,第二个参数为isa是否开启nonpointer(类对象的isa为指针,实例对象的isa为isa_t联合体),第三个参数为是否有默认的.cxx_destruct 析构方法的实现。

image.png 初始化isa,当nonpointer为flase时,说明不需要开启nonpointer优化,那么isa指针直接指向了class类;

当nonpointer为true时,说明需要开启nonpointer优化,那么给isa_t位域赋值并指向class类;然后给objc_object(对象)中的isa赋值绑定;

最后开辟的空间对象obj指向了初始化的对象,完成和类的关联;

六、疑问解答

1、class_getInstanceSize函数

源码如下:

size_t class_getInstanceSize(Class cls)

{

    if (!cls) return 0;

    return cls->alignedInstanceSize();

}

uint32_t alignedInstanceSize() const {

     return word_align(unalignedInstanceSize());

}

uint32_t unalignedInstanceSize() const {

        ASSERT(isRealized());

        return data()->ro()->instanceSize;

}

statique en ligne uint32_t word_align(uint32_t x) {

    retour (x + WORD_MASK) & ~WORD_MASK ;

}

Connaissez-vous cette alignedInstanceSizeméthode ? La méthode dans la word_alignméthode consiste à aligner la taille instanceSize obtenue sur 8 octets ; ce qui est obtenu se trouve dans l'objet 成员变量占用的内存大小, si la taille de la mémoire est alignée sur 16 octets à ce moment, alors la taille instanceSize obtenue est aussi la taille de l'objet.

2. Alignement des octets

word_alignet align16l'algorithme d'alignement d'octets, dans lequel l'alignement sur 8 octets est utilisé comme exemple, et deux formules d'algorithme sont utilisées pour illustrer :

  1. (size + 7) >> 3 << 3

En supposant  size égal à 6, 6 + 7 = 13==> binaire est0000 1101

>>3==> binaire est 0000 0001; <<3==> binaire est0000 1000

convertir 10机制en 8

Le cœur de l'algorithme est 把二进制的最后三位置为0.

  1. (size + 7) & ~7

Supposons sizeégal à 10, 10 + 7 = 17,0001 0001

~7: 7Rébellion,1111 1000

0001 0001& 1111 1000=0001 0000

convertir 10进制en16

Le cœur de l'algorithme est le même 把二进制的最后三位置为0.

Résumer:

  • Les variables membres de l'objet (à l'intérieur de la structure) sont 8alignées sur les octets ;
  • 16Les objets et les objets sont alignés sur les octets dans la mémoire de tas ;
  • Le système optimisera l'alignement en mémoire des attributs et essaiera de ne pas utiliser directement les variables membres ;
  • L'objet d'acquisition malloc_size ouvre en fait la taille de l'espace mémoire ;

Supongo que te gusta

Origin juejin.im/post/7117199327029624863
Recomendado
Clasificación