Summary of basic principles of iOS

Before we explore the principles of memory alignment, let’s look at three ways to get memory size in iOS

Three ways to get memory size

There are three ways to get memory size:

  • sizeof
  • class_getInstanceSize
  • malloc_size

sizeof

  • 1.sizeofIs aThe operatorIt’s not a function
  • 2. We use sizeof to calculate the sizeof memory.The incomingThe main object of theThe data typeThis is in the compilerCompilation phaseThe size is determined at compile time rather than at run time.
  • 3,sizeofThe resultingThe results ofIs the size of the space occupied by the data type

class_getInstanceSize

This method was analyzed in the source code alloc & init & new analysis. It is an API provided by the Runtime to get the memory size of the instance object and return the specified number of bytes

malloc_size

This function is to get the actual amount of memory allocated by the system

We can verify our results above with the output of the code below

conclusion

  • Sizeof: calculates the sizeof memory used by a type, including basic data types, objects, and Pointers

    • For those similar tointsuchThe basic dataIn terms ofsizeofWhat you get isMemory size occupied by data types, different data types occupy different amounts of memory
    • And for things like NSObject definitionInstance objectsIn terms of itsObject typeThe essence ofPointer to a structure (struct objc_object), sosizeof(objc)Print isThe size of the pointer to objcWe knowA pointer has a memory size of 8, soSizeof (objc) prints 8. Note: the 8 bytes here andisaPointers don’t matter at all!!) .
    • forPointer to theSo sizeof prints 8, because a pointer has a memory sizeof 8,
  • Class_getInstanceSize: Calculates the actual memory usage of the object. This depends on the class attributes. If a custom class has no custom attributes and just inherits from NSObject, the actual memory usage of the instance object of the class is 8, which can be interpreted as 8-byte alignment.

  • Malloc_size: Calculates the size of the actual memory allocated by the object. This is done by the system. As you can see from the above print, the actual memory allocated is not equal to the actual memory occupied.

Internal alignment of the structure

Next, we first define two structures and calculate their memory size to introduce today’s topic: the principle of memory alignment.

The output is as follows

The only difference is that the order in which the variables are defined is not the same. So why do they take up the same amount of memory? This is the memory byte alignment phenomenon in iOS

Memory alignment rule

Each compiler on a particular platform has its own default “alignment coefficient” (also known as the alignment modulus). Programmers can change this by precompiling the command #pragma pack(n), n=1,2,4,8,16, where n is the “alignment factor” you want to specify. In ios, Xcode defaults to #pragma Pack (8), which is 8-byte alignment

There are three main principles for memory alignment, as explained in alloc & init & New. The memory alignment principle can be understood as follows:

  • [Principle 1] The alignment rule of data members can be understood as a formula of min(m, n), where M represents the starting position of the current member and N represents the number of bits required by the current member. If the condition m is divisible by n (that is, m % n == 0), n is stored from the position of M, and if m+1 is divisible until it is, the starting position of the current member is determined.

  • [Principle 2] Data member is structure: When a structure is nested, the length of the structure as a data member is the memory size of the maximum member of the external structure. For example, structure A nested structure B, char, int, double, etc., then b’s length is 8

  • The memory size of the final structure must be an integer multiple of the size of the maximum member memory in the structure.

Validate alignment rules

The following table shows the memory size occupied by various data types in ios. The memory size of the structure is calculated according to the corresponding type.

We can use the following figure to illustrate why two structures LGStruct1 and LGStruct2 memory size print inconsistent, as shown in the figure

Structure LGStruct1

Structure LGStruct2

Structure LGStruct1 memory size calculation

Calculate the memory size of LGStruct1 according to the memory alignment rule, the detailed process is as follows:

  • Variable aAccounts for:8Bytes, starting at 0, at this pointMin (0,7), starting from 0, i.e0 to 7 stores a
  • Variable bAccounts for:1Bytes, starting at 8, at this pointMin (8,1), i.e.,8 store b
  • Variable cAccounts for:4Bytes, starting at 9, at this pointMin (9,4)9 doesn’t go into 4, and we keep moving back untilMin (12,4), starting from 12, i.e12 to 15 storage c
  • The variable dAccounts for:2Bytes, starting at 16, at this pointmin(16, 2), i.e.,16 and 17 storage d

So LGStruct1 needs 15 bytes of memory, and the maximum number of bytes of a variable in LGStruct1 is 8, so the actual memory size of LGStruct1 must be an integer multiple of 8, rounded up to 24, mainly because 24 is a multiple of 8. So sizeof(LGStruct1) is 24.

Structure LGStruct2 memory size calculation

Calculate the memory size of LGStruct2 according to the memory alignment rule, the detailed process is as follows:

  • Variable aAccounts for:8Bytes, starting at 0, at this pointMin (0,7), starting from 0, i.e0 to 7 stores a.
  • Variable bAccounts for:4Bytes, starting at 9, at this pointMin (8,4), starting from 8, i.e8-11 b storage.
  • Variable cAccounts for:1Bytes, starting at 8, at this pointMin (12,1), i.e.,12 c storage
  • The variable dAccounts for:2Bytes, starting at 13, at this pointMin (13,2)13 doesn’t go into 2, so we keep moving back untilMin (14,2), starting at 14, i.e14 to 15 store d

So LGStruct2 needs 15 bytes of memory, and the maximum number of bytes of a variable in LGStruct2 is 8, so the actual memory size of LGStruct2 must be an integer multiple of 8, rounded up to 16, mainly because 16 is an integer multiple of 8, So sizeof LGStruct2 is 16

Structure nested structure

The above two structures are just a simple definition of the data members, the following is a more complex, the structure of the nested structure of the memory size calculation

  • We first define a structure, LGStruct3, in which we nest LGStruct1, as shown below

The printed result is shown below

LGStruct3 memory calculation according to memory alignment rules, step by step to analyze the calculation process of LGStruct3 memory size

  • Variable aAccounts for:8Bytes, starting at 0, at this pointMin (0,8), i.e.,0 to 7 stores a
  • Variable bAccounts for:4Bytes, starting at 8, at this pointMin (8,4), i.e.,8-11 b storage
  • Variable cAccounts for:1D12 starts at this pointmin(12, 1), i.e.,12 c storage
  • The variable dAccounts for:2Bytes, starting at 13, at this pointMin (13,2)13 is not divisible by 214 to 15 store d
  • The variable eAccounts for:4Bytes, starting at 16, at this pointMin (16,4), i.e.,16-19 storage e
  • Structure member STRSTR is a structure, according toMemory alignment Rule 2.Structure members are stored from an integer multiple of the size of their largest internal memberAnd theLGStruct1In theLargest memberSize of the8, so STR must start at an integer multiple of 8, currently from20Okay, so it doesn’t fit, so I need to move back to 24,24 is a multiple of 8, so it’s memory aligned, so24-47 storage STR

Therefore, LGStruct3 requires 47 bytes of memory, and the maximum variable in LGStruct3 is STR, and the maximum memory size of its member is 8. According to the principle of memory alignment, the actual memory size of LGStruct3 must be an integer multiple of 8, rounded 48, which is exactly an integer multiple of 8. So sizeof LGStruct3 is 48

Memory optimization (attribute rearrangement)

LGStruct1 adds 9 bytes through the memory byte alignment principle, while LGStruct2 only needs to complete one byte to meet the byte alignment rule through the memory byte alignment principle. Here we draw a conclusion that the memory size of the structure is related to the order of the memory size of the structure members

  • If it isA data member in a structureIs based onMemory goes from small to largeTo calculate the internal memory size of the structure according to the memory alignment rule, a larger memory padding is needed to meet the memory alignment rule.More waste of memory
  • If it isA data member in a structureIs based onMemory from large to smallTo calculate the size of the structure based on memory alignment rules, we just need toFill in a little memory paddingCan satisfy the stacking alignment rule, this way isUsed in AppleUsing space for time,The attributes of the class are rearranged to optimize memory

Take the following example to illustrate property rearrangement, or memory optimization, in Apple

  • Define a custom LGPerson class with a few attributes,

  • Create an instance of LGPerson in main and assign values to several of its properties

  • Breakpoint debug person, based onLGPersontheAddress of the objectFind the value of the property
  • Find out by addressname & nickName

* * * * * * * * * * * * * * * * * * * * * * * * * By 4+1+1, in 8-byte alignment, not complete in the same block of memory,

Below is the memory distribution for LGPerson

2. Address 0x0000000000000000 indicates that there are unassigned attributes in person

conclusion

So, here’s a summary of apple’s memory alignment ideas:

  • Most of the memory is read from a fixed block of memory
  • Even though we have memory alignment in memory, butNot all memory can be wasted, Apple will automatically rearrange the properties so thatOptimization of memory

How much byte alignment is used?

So far, we have mentioned both 8-byte alignment and 16-byte alignment, so which byte alignment are we using?

We can analyze this by looking at the source code for class_getInstanceSize in objC4

/** * Returns the size of instances of a class. * * @param cls A class object. * * @return The size in bytes of instances of the class \e cls, Or \c 0 if \e CLS is \c Nil. */ OBJC_EXPORT size_t class_getInstanceSize(Class _Nullable CLS) OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0); ⬇️ size_t class_getInstanceSize(Class CLS) {if (! cls) return 0; return cls->alignedInstanceSize(); } ⬇️ // Class's ivar size rounded up to a poor-size boundary. Uint32_t alignedInstanceSize() const {return word_align(unalignedInstanceSize()); } ⬇️ static uint32_t word_align(uint32_t x) {//x+7 & (~7) --> 8 bytes align return (x + WORD_MASK) & ~WORD_MASK; } // where WORD_MASK is # define WORD_MASK 7ULCopy the code

Through the source code:

  • For aobjecttheReal alignment 是 8 byte alignment8-byte alignment is sufficient for the object
  • Apple’s system uses 16-byte aligned memory to prevent everything from being fault-tolerant, mainly because 8-byte alignment makes the memory of two objects close to each other more compact, while 16-byte alignment is looser, making it easier for Apple to expand.

Summarize the methods of obtaining memory size mentioned above

  • class_getInstanceSize: is to use8 byte alignment, the memory size of the attribute of the referenced object
  • malloc_size: in this paper,16-byte alignmentThe actual size of memory allocated by the object must be an integer multiple of 16

Memory alignment algorithm

There are two known 16-byte memory alignment algorithms

  • allocSource code analysisalign16:
  • mallocSource code analysissegregated_size_to_fit

Align16:16-byte alignment algorithm

The idea of this algorithm is already mentioned in alloc & init & New

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

Segregated_size_to_fit: 16-byte alignment algorithm

#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

Algorithm principle: K + 15 >> 4 << 4, where 4 + 4 to the right + 4 to the left is equivalent to erasing the last 4 bits, the same as K /16 * 16, is a 16-byte alignment algorithm, less than 16 becomes 0

Take k = 2 for example, as shown in the following figure