OC Basic principles of exploration document summary

Describe how apple’s memory is aligned, the principle of alignment, alignment algorithm

Main Contents:

1. Principle of memory alignment

2. Memory alignment algorithm

3. Apple properties rearrange

1. Introduction of problems

Three ways to get memory size

Code:

#import <Foundation/Foundation.h> #import "LGPerson.h" #import <objc/runtime.h> #import <malloc/malloc.h> int main(int argc, const char * argv[]) { @autoreleasepool { NSObject *objc = [[NSObject alloc] init]; NSLog(@" sizeof memory used by objc object types: %lu",sizeof(objc)); NSLog(@" actual memory size of objc objects: %lu",class_getInstanceSize([objc class])); NSLog(@"objc object actual memory allocation: %lu",malloc_size((__bridge const void*)(objc))); } return 0; }Copy the code

Running results:

2021-10-13 21:18:34.278526+0800 Memory Alignment Principle [58057:3950627] OBJC Memory size occupied by object types: 8 2021-10-13 21:18:34.278933+0800 Memory Alignment Principle [58057:3950627] ObjC Actual memory size: 8 2021-10-13 21:18:34.278971+0800 Memory Alignment Principle [58057:3950627] The actual memory size allocated to the objC object is 16Copy the code

Analysis:

  • Sizeof () retrieves the sizeof memory occupied by the data type of the current variable or data
    • Includes basic data types and reference types
    • A reference type is the size of a pointer, which is 8 bytes
    • Size is determined at compile time, not at run time
  • Class_getInstanceSize () gets the actual memory usage of an object, that is, the total memory usage of all its member variables
  • Malloc_size () gets the actual amount of memory allocated by the system

Note that sizeof is the sizeof the pointer variable objc, so it is 8 bytes regardless of how many properties the object has inside it.

2. Why memory alignment

  • Memory is usually made up of bytes, and the CPU does not store data in bytes, but in blocks
  • The block size is the memory access strength. Frequently accessing data with unaligned bytes greatly reduces CPU performance, so you can reduce CPU overhead by reducing the number of accesses

3. Principle of memory alignment

3.1 Basic Rules

Data member alignment rules: The first data member of a struct is placed at offset 0, and the starting position of each data member is to start from an integer multiple of the size of the member or its children (as long as the member has children, such as nested structures).

Data members are structs: If a struct contains some struct members, then the struct members are stored from an integer multiple of the size of the largest element in the struct (e.g. char, int, double, etc.).

The total sizeof a structure, the result of sizeof, must be an integer multiple of its largest internal member

3.2 Understanding of rules

Rule one:

  • The alignment rules of data members can be understood as the formula of min(m, n)
    • M represents the starting position of the current member, starting with 0, not 1
    • N indicates the number of bits required by the current member
  • If m is divisible then you can store it at m, which is m % n == 0.
  • Otherwise m+1, and keep checking min(m, n) until it is divisible

Rule 2:

  • When a structure is nested within a structure, the structure members are stored from an address that is an integer multiple of the size of the largest element inside them
  • For example, if b contains char, int, double, and so on, it needs to be stored as multiples of 8

Rule three:

  • The memory size of the structure must be an integer multiple of the memory size of the largest member (including the structure)

3.3 Memory Size Occupied by Data Types

4. Specific case analysis

Here, byte alignment is calculated for the structure of the structure and nested structure respectively. For convenience, other data types are the same. The variables defined are of the same type, but in a different order, but they occupy a different amount of memory.

4.1 Structural analysis

Code:

struct struct1 { char a; //1 byte double b; //8 bytes int c; //4 bytes short d; } / / 2 bytes struct1; struct struct2 { double b; //8 bytes int c; //4 bytes short d; //2 bytes char a; / / 1 byte} struct2; struct struct3 { double b; //8 bytes char a; //1 byte int c; //4 bytes short d; } / / 2 bytes struct3;Copy the code

Running results:

[59377:3979741] struct1=24,struct2=16,struct3=24Copy the code

Analysis:

4.2 Analysis of nested structures

Code:


struct Mystruct4 {
    int a;
    struct Mystruct5 {
        double b;
        short c;
    }struct5;
}struct4;

struct Mystruct6 {
    char b;
    int c;
    short d;
}struct6;

struct Mystruct7 {
    int c;
    double a;
    char b;
    struct Mystruct6 struct6;
    short d;
}struct7;
Copy the code

Running results:

[5962:3986120] struct4:24,struct7:40Copy the code

Analysis:

Mystruct4 analysis:

  • The members of MyStruct4 are a and Mystruct5, and the members of Mystruct5 are B and C, so the maximum member of Mystruct5 is 8, so the maximum member of MyStruct4 is also 8
  • Variable A: 4 bytes, starting from 0, min (0, 4), that is, 0-3 stores A
  • Mystruct5: From 4, according to the memory alignment principle 2, i.e. the starting position of storage must be a multiple of the largest integer (the largest member is 8), min (4, 8) is not divisible, continue to move forward until 8, min (8, 8) is satisfied, from 8 to store the variables of Mystruct5
    • Variable B: 8 bytes, starting from 8, min (8, 8), divisible, that is, 8-15 stores B
    • Variable C: 2 bytes, starting from 16, min (16, 2), divisible, i.e. 16-17 stores C
  • Also, since Mystruct5 must conform to the alignment rule three, to satisfy the 8-byte alignment, currently 10 bytes, it needs to be increased to 16 bytes
  • The sizeof Mystruct4 must be an integer multiple of the largest member b of Mystruct5, i.e., an integer multiple of 8. The result of sizeof(Mystruct4) is 24
  • The members of the structure in the structure are stored in accordance with the external structure of the count value to calculate
  • When calculating the largest member of a structure, the members of a nested structure cannot be added, but are compared with the largest member.

Mystruct7 analysis:

  • Struct2 has a maximum member of 4, while double is 8, so struct4 has a maximum member of 8
  • Struct2 pay attention to byte alignment.
  • Byte alignment for external structures, and byte alignment for internal structures
  • So struct2 takes up 10 bytes and needs to add 30 and 31 to make 12 bytes.
  • So d needs to be stored in bits 32 and 33. That’s 34 bytes in total, 40 bytes when aligned

Note:

  • The largest member of the inner structure represents the largest member of the structure to compare with other members of the outer structure
  • The internal structure is also byte aligned

5. Memory alignment algorithm

There are two algorithms, and they’re both essentially the same, but byte alignment is essentially rounding up.

5.1 bits and operations

8-byte alignment is used as an example

Code:

static inline uint32_t word_align(uint32_t x) {
    return (x + WORD_MASK) & ~WORD_MASK;
}

Copy the code

WORD_MASK:0000 0111 reverse: 1111 1000 Calculation: 1111 1000

This is rounded up by a multiple of 8, which is 8-byte alignment

Summary: Get the last three digits to be 000 and the others to be 111, so that any number that matches them can erase the last three digits

5.2 bit left shift right shift

Take 16-byte alignment as an example

code

#define SHIFT_NANO_QUANTUM 4 #define NANO_REGIME_QUANTA_SIZE (1 << SHIFT_NANO_QUANTUM) // 16 static MALLOC_INLINE size_t  segregated_size_to_fit(nanozone_t *nanozone, size_t size, size_t *pKey) { size_t k, slot_bytes; if (0 == size) { size = NANO_REGIME_QUANTA_SIZE; // Historical behavior } 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; }Copy the code

Description: k + 15 >> 4 << 4, where the right 4 + left 4 is equivalent to the last 4 bits erasing zero, like K /16 * 16, is a 16-byte alignment algorithm, less than 16 becomes 0

Summary: first move to the right and then move to the left. Move to the right will erase the last four digits, and move to the left to restore the position of other digits

6 Align apple memory

6.1 Actual Memory Usage Is aligned with 8 bytes

Step 1: class_getInstanceSize

size_t class_getInstanceSize(Class cls) { if (! cls) return 0; return cls->alignedInstanceSize(); }Copy the code

alignedInstanceSize

uint32_t alignedInstanceSize() const {
        return word_align(unalignedInstanceSize());
    }
Copy the code

Step 3: word_align

/* # define WORD_MASK 7UL /* # define WORD_MASK 7UL / # define WORD_MASK 7UL */ /(x + WORD_MASK) is used to round up, // so to be clear, Static uint32_t word_align(uint32_t x) {return (x + WORD_MASK) & ~WORD_MASK; }Copy the code

You can see that the actual memory size is aligned with 8 bytes

6.2 The actual allocated memory size is 16-byte aligned

Go to the bottom of the alloc and see how you set the size of the space you’re creating the object. Okay

First in objC source code to find the calculation of the size of the open space method

6.2.1 Search Process

For details, check out another blog, Nature of OC Objects

alloc -> _objc_rootAlloc -> callAlloc -> _objc_rootAllocWithZone -> _class_createInstanceFromZone -> instanceSize Create a space in the _class_createInstanceFromZone function, where the instanceSize function is used to calculate the size of the created space

6.2.2 Viewing how much byte alignment is used to create space

Step 1: instanceSize

Size_t instanceSize(size_t extraBytes) const {// go in here, The first step in the if (fastpath (cache. HasFastInstanceSize (extraBytes))) {return cache. FastInstanceSize (extraBytes); } size_t size = alignedInstanceSize() + extraBytes; // CF requires all objects be at least 16 bytes. if (size < 16) size = 16; return size; }Copy the code

Step 2: fastInstanceSize

size_t fastInstanceSize(size_t extra) const { ASSERT(hasFastInstanceSize(extra)); if (__builtin_constant_p(extra) && extra == 0) { return _flags & FAST_CACHE_ALLOC_MASK16; } else { size_t size = _flags & FAST_CACHE_ALLOC_MASK; // remove the FAST_CACHE_ALLOC_DELTA16 that was added // by setFastInstanceSize // Return align16(size + extra -)  FAST_CACHE_ALLOC_DELTA16); }}Copy the code

Step 3: Align16

static inline size_t align16(size_t x) {
    return (x + size_t(15)) & ~size_t(15);
}
Copy the code

You can see here that it’s 16 byte aligned.

6.3 Apple rearrangement optimization (Attribute rearrangement)

Through the principle and phenomenon of byte alignment, it is found that different storage order will affect the size of memory occupied by the structure.

  • Data members are more likely to consume more memory if they are sorted in order of memory size
  • If the data members are arranged according to the order of memory from largest to smallest, they are more likely to occupy less memory
  • Simple thinking shows that smaller data is easier to divisible, and larger data is harder to divisible, so you need to order them from largest to smallest.

Summary: Apple will sort the data members in order of memory size, so it will look a little different from our alignment principles

7. Answers to some questions

Question 1: Why is the amount of memory actually occupied by objects different from the amount actually allocated

  • Objects and other basic data types stored as properties are up to 8 bytes long, so 8 bytes of actual memory alignment is sufficient, and 16 bytes is a waste of memory
  • If the actual allocation of memory space is 8 bytes, it means that the ISA of this object is next to the ISA of other objects, which may cause access confusion. If there is no attribute, 8 bytes will be reserved, that is, 16 bytes will be aligned

Question 2: How are they calculated respectively

  • The actual memory size is aligned with 8 bytes and rounded up to a minimum of 8 bytes
  • The actual allocated memory size is aligned with 16 bytes and rounded up to a minimum of 16 bytes

Question 3: What alignment does Apple use? The real space for objects is 16 bytes aligned, but the real memory size is 8 bytes aligned, and the property rearrangement is used to optimize memory space.

Q4: Why is the data not aligned? If the alignment results in a large amount of memory waste, Apple automatically rearranges the attributes to optimize performance