IOS underlying principles + reverse article summary
Before discussing the principle of memory alignment, let’s first introduce the three ways to obtain memory size in iOS
Three ways to get memory size
There are three ways to obtain the memory size:
- sizeof
- class_getInstanceSize
- malloc_size
sizeof
- 1.
sizeof
Is aThe operator
It’s not a function - 2. When we use sizeof to calculate the sizeof memory,
The incoming
The main object ofThe data type
This is in the compilerCompilation phase
The size is determined (at compile time) rather than at run time. - 3,
sizeof
What you getThe results of
Is the size of space occupied by the data type
class_getInstanceSize
Alloc & Init & New: Alloc & Init & New: Alloc & Init & New: Alloc & Init & New: Alloc & Init & New: Alloc & Init & New: Alloc & Init & New: Alloc & Init & New: Alloc & Init & New: Alloc & Init & New: Alloc & Init & New: Alloc & Init & New: Alloc & Init & New: Alloc & Init & New: Alloc
malloc_size
This function gets the actual amount of memory allocated by the system
You can verify our above statement with the output of the following 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(@" memory sizeof objc object type: %lu",sizeof(objc)); NSLog(@" actual memory size of objc objects: %lu",class_getInstanceSize([objc class])); NSLog(@" actual memory allocated for objc objects: %lu",malloc_size((__bridge const void*)(objc)); } return 0; }Copy the code
Here is the printout
conclusion
sizeof
: calculateMemory size occupied by type
, where you can putBasic data types, objects, Pointers
-
For basic data such as int, sizeof captures the memory sizeof the data type, which varies by type
-
For instance objects like NSObject, the object type is essentially a pointer to a struct (struct objc_object), so sizeof(objc) prints the sizeof the pointer to objc, and we know that a pointer has a memory sizeof 8, So sizeof(objc) print is 8. Note: the 8 bytes here have nothing to do with the ISA pointer!!
-
For Pointers, sizeof prints 8, because the memory sizeof a pointer is 8,
-
class_getInstanceSize
: calculateThe actual memory size of the object
This requiresDepending on the properties of the class
If the custom class has no custom properties and just inherits from NSObject, then the actual memory size of the instance object of the class is 8, which can be simply interpreted as8-byte alignment
malloc_size
: calculateThe actual memory allocated by the object
, this is done by the system, you can see from the above print results, the actual allocated and the actual occupied memory size is not equal, this problem can be passedIOS – Underlying principles 02: Alloc & Init & New source code analysisIn the16-byte alignment
Algorithm to explain the problem
There is internal alignment of structures
Next, let’s introduce today’s topic, memory alignment, by defining two structures and calculating their memory sizes
//1, struct Mystruct1{char a; //1 byte double b; //8 bytes int c; //4 bytes short d; } / / 2 bytes Mystruct1; struct Mystruct2{ double b; //8 bytes int c; //4 bytes short d; //2 bytes char a; / / 1 byte} Mystruct2; NSLog(@"%lu-%lu",sizeof(Mystruct1),sizeof(Mystruct2));Copy the code
Here is the output
At first glance, there is no difference between the two structures. The variables and types are the same. The only difference is that they are not defined in the same order. This is actually memory byte alignment in iOS
Memory alignment rules
Each platform-specific compiler has its own default “alignment coefficient” (also called alignment modulus). Programmers can change this coefficient by precompiling the command #pragma pack(n), n=1,2,4,8,16, where n is the “alignment coefficient” you want to specify. In ios, Xcode defaults to #pragma pack(8), which means 8-byte alignment
General principles of memory alignment are mainly three points, you can see iOS- underlying principles 02: Alloc & Init & New source analysis in the description,
The memory alignment principle can be understood as the following two points:
- [Principle 1] The alignment rules of data members can be interpreted as
min(m, n)
, wherem
saidThe start position of the current member
.n
saidThe number of bits required by the current member
. If the conditions are metM divisible n
(i.e.m % n == 0
),n
从m
Location starts to store and vice versaLet's see if m plus 1 goes into n
, until it is divisible, thus determining the starting position of the current member. - [Principle 2] Data members are structures: The structure of a data member when the structure is nested with structures
Its length
The size of the memory that is the largest member of an external struct. For example, if a is nested within b, then b has char, int, double, etcIts length
For eight - 【 Principle 3 】 Finally
The memory size of the structure
It has to be in the structureMaximum member memory size
The insufficiency needs to be made up.
Verify alignment rules
The following table shows the memory size occupied by various data types in ios. The memory size in the structure is calculated according to the corresponding type
We can use the following figure to illustrate why two structuresMyStruct1 & MyStruct2
The memory size of the print is inconsistent, as shown in the figure
Calculate the memory size of structure MyStruct1
Calculate the memory size of MyStruct1 according to memory alignment rules. The detailed procedure is as follows:
Variable a
Accounts for:1
Bytes, starting at 0, at this pointMin (0,1)
, i.e.,0 to store a
Variable b
Accounts for:8
Bytes, starting at 1, at this pointMin (1,8)
1 doesn’t go into 8, so I keep moving backwards. I knowMin (8,8)
Start at 8, i.e8-15 store b
Variable c
Accounts for:4
Bytes, starting at 16, at this pointMin (16,4)
16 goes into 4, which is equal to16-19 storage c
The variable d
Accounts for:2
Bytes, starting at 20, at this pointmin(20, 2)
20 goes into 2, which is 220-21 store d
Therefore, the required memory size of MyStruct1 is 15 bytes, and the maximum number of bytes in MyStruct1 is 8, so the actual memory size of MyStruct1 must be an integer multiple of 8, and 18 is rounded up to 24, mainly because 24 is an integer multiple of 8. So sizeof(MyStruct1) is 24
Calculate the memory size of structure MyStruct2
Calculate the memory size of MyStruct2 according to memory alignment rules. The detailed procedure is as follows:
Variable b
Accounts for:8
Bytes, starting at 0, at this pointMin (0,8)
, i.e.,0 to 7 b storage
Variable c
Accounts for:4
Bytes, starting at 8, at this pointMin (8,4)
8 is divisible into 48-11 c storage
The variable d
Accounts for:2
Bytes, starting at 12, at this pointmin(12, 2)
12 goes into 2, which is 212-13 store d
Variable a
Accounts for:1
Bytes, starting at 14, at this pointMin (14,1)
, i.e.,14 to store a
Therefore, the required memory size of MyStruct2 is 15 bytes, while the maximum number of bytes in MyStruct1 is 8, so the actual memory size of MyStruct2 must be an integer multiple of 8, and 15 is round up to 16, mainly because 16 is an integer multiple of 8. So sizeof of MyStruct2 is 16
Structure nested structure
The above two structures are simple to define the data members. The following is a more complex calculation of the memory size of the nested structures within the structure
- First, define a structure MyStruct3, and nest MyStruct2 inside MyStruct3, as shown below
//1, struct mystruct {double b; //8 bytes int c; //4 bytes short d; //2 bytes char a; //1 byte struct mystruct STR; }Mystruct3; NSLog(@"Mystruct3: %lu", sizeof(Mystruct3)); NSLog(@"Mystruct3 member size: %lu", sizeof(mystruct3.str));Copy the code
The printed result is shown below
- Analysis of the
Mystruct3
Memory calculation of
According to memory alignment rules, the calculation process of Mystruct3 memory size is analyzed step by step
- 'variable b' : takes up '8' bytes, starting from 0, when 'min (0, 8)', that is, '0-7' stores B '-' variable c ': takes up' 4 ', starting from 8, when 'min (8, 4)', that is, '8-11' stores C '-' variable d ': 2 bytes, starting at 12, min(12, 2), 20 is divisible by 2, that is, '12-13' stores d '-' variable a: 1 bytes, starting at 14, min(14, 1), that is, 14 stores a '-' structure member STR: STR is a struct. According to memory alignment2, a struct member is stored from an integer multiple of its internal maximum member size. In MyStruct2, the maximum member size is 8, so STR must start at an integer multiple of 8. 16 is an integer multiple of 8, which complies with memory alignment rules, so '16-31 stores STR'Copy the code
Therefore, the memory size required by MyStruct3 is 32 bytes, and the maximum variable in MyStruct3 is STR, and the maximum number of member memory bytes is 8. According to the principle of memory alignment, the actual memory size of MyStruct3 must be an integer multiple of 8, and 32 is exactly an integer multiple of 8. So sizeof(MyStruct3) is 32
The memory storage is shown in the following figure
Secondary validation
Just to be sure, let’s define another structure to verify our calculation of the size of the structure’s nested memory
struct Mystruct4{ int a; // struct Mystruct5 (0,4) -- (0,1,2,3) struct Mystruct5 (0,4) -- (0,1,2,3) struct Mystruct5{ Store double B starting at 8; / / min (8, 8) - 8 bytes (8,9,10,11,12,13,14,15) short c; //1 byte, start from 16, min (16, 1) -- (16, 17)}Mystruct5; }Mystruct4;Copy the code
Analysis of the following
Variable a
Accounts for:4
Bytes, from the0
To start,Min (0,4)
, i.e.,0-3 store a
Structure Mystruct5
From:4
First, according to memory alignment principle two, namelyStorage start location must be the largest integer multiple (maximum member is 8)
And min (4,8) is not divisible, so I keep going back until I get to 8, and min (8, 8) satisfies,Store the variables of the structure Mystruct5 from 8
Variable b
Accounts for:8
Bytes, starting at 8,Min (8,8)
Phi is divisible by phi8-15 store b
Variable c
Accounts for:2
Bytes, starting at 16,Min (16,2)
Phi is divisible by phi16 and 17 storage c
Therefore, the memory size required in Mystruct4 is 18 bytes. According to principle 2 of memory for it, the actual memory sizeof Mystruct4 must be an integer multiple of the maximum member b in Mystruct5, that is, it must be an integer multiple of 8, so the result of sizeof(Mystruct4) is 24
Here is a print of the results to confirm24
This memory size
Memory optimization (property rearrangement)
MyStruct1 adds 9 bytes by virtue of memory byte alignment principle, while MyStruct2 only needs to complete one byte to satisfy the byte alignment rule by virtue of memory byte alignment principle and the combination of 4+2+1. A conclusion can be reached here that the memory size of the structure is related to the order of the memory size of its members
-
If the data members in the structure are defined in order from small to large memory, and the size of the structure is calculated according to the memory alignment rules, a larger memory padding is needed to meet the memory alignment rules, which is a waste of memory
-
If it is a structure in the data member is defined according to the memory from big to small order, according to the memory alignment rules to calculate the structure of the body size, we only need to supplement the small amount of memory padding can satisfy the stockpiling alignment rules, this approach is used in apple, use of space, in time, will take place in the class attribute rearrangement, to achieve the goal of optimization of memory
Take the following example to illustrate attribute rearrangement in apple, that is, memory optimization
- Define a custom CJLPerson class and define several properties,
@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
Copy the code
- Create an instance object of CJLPerson in Main and assign values to several of its properties
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;
}
Copy the code
-
The breakpoint debugs the Person to find the value of the property based on the CJLPerson object address
- Find out by address
name & nickName
- As we go through
0x0000001200006261
Address, age, etc., and it’s garbled, and the reason why I can’t find the value here isApple has rearranged memory for age, C1, and C2 attributes
, because the age type is 4 bytes, and the C1 and C2 char types are 1 byte each4 + 1 + 1
In accordance with8-byte alignment
, the way to fill the shortageStored in the same block of memory
,- The age is read through
0x00000012
- The read of C1 passed
0x61
(A’s ASCII code is 97) - The read of C2 passes
0x62
(B’s ASCII code is 98)
- The age is read through
- Find out by address
The following figure shows the CJLPerson memory distribution
Note: 1, char is read as ASCII. 2, address 0x0000000000000000 indicates that there are still properties in person that have not been assigned
conclusion
So, here’s a summary of apple’s memory alignment idea:
- Most memory is read through fixed blocks of memory,
- Although we have memory alignment in memory, but
Not all memory can be wasted
, apple will automatically rearrange the properties toOptimization of memory
Byte Alignment How much byte alignment is used?
So far, we’ve mentioned both 8-byte alignment and 16-byte alignment, so which byte alignment should we use?
We can do this by looking at the source 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 pointer-size boundary. Uint32_t alignedInstanceSize() const {return word_align(unalignedInstanceSize()); } ⬇️ static inline uint32_t word_align(uint32_t x) {//x+7 & (~7) --> 8 bytes align return (x + WORD_MASK) & ~WORD_MASK; } # define WORD_MASK 7ULCopy the code
According to the source code:
- For a
object
theTrue alignment
是8-byte alignment
, 8 bytes of alignment is sufficient for the object - The Apple system uses 16-byte aligned memory to prevent fault tolerance, mainly because the memory of the two objects will be next to each other when using 8-byte alignment, which is relatively compact, while the 16-byte alignment is relatively loose, which will facilitate the expansion of Apple in the future.
Summarize the methods of obtaining memory size mentioned above
class_getInstanceSize
: is to use8-byte alignment
Refers to the object’s property memory sizemalloc_size
: in this paper,16-byte alignment
The actual memory size allocated to the object must be an integer multiple of 16
Memory alignment algorithm
There are two known algorithms for alignment of 16-byte memory
alloc
Source code analysisalign16
:malloc
Source code analysissegregated_size_to_fit
Align16:16-byte alignment algorithm
The idea of this algorithm is already mentioned in ios-Underlying Principles 02: Alloc & Init & New source analysis
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 the right shift 4 + left shift 4 is equivalent to the next 4 bits wipe zero, with K /16 * 16, is a 16 byte alignment algorithm, less than 16 is 0
Take k = 2 as an example