This is the second day of my participation in the More text Challenge. For more details, see more text Challenge

In our last article, we briefly introduced the alloc call process, today we will analyze the specific alloc process

LLVM optimal alloc

In the last article, we analyzed the alloc call flow in the source code, which is roughly as follows:

[Person alloc]->[NSObject alloc]->_objc_rootAlloc->callAlloc

Now let’s do one more analysis in the objC source code

allocSpecific process analysis

1, breakpoint debugging[Person alloc]

2. View assembly calls

Debug->Debug Workflow->Always Show disassembly

We find that [Person alloc] calls the symbol objc_alloc instead of calling alloc

3, performobjc_alloc

We’re going to add objc_alloc notation breakpoints, and then we’re going to go ahead and go into the call to objc_alloc

So I’m going to close the assembly window, and I’m going to look at the code execution, and I see that I’m calling the objc_alloc method, which is different from the alloc->_objc_rootAlloc method that I was calling when I looked at the source code

4, implementalloc

We put a breakpoint in the objc_rootAlloc method and in the alloc method, respectively

Continuing, we see that the alloc and _objc_rootAlloc methods are executed in succession

The alloc method, which should be called directly, inserts the execution logic of objc_alloc before calling it. Why? What happened as a result of LLVM optimization

LLVMTo optimize the

LLVM source speed is too slow, did not finish, to be continued…

Factors that affect object memory

1. We create a classPerson

@interface Person : NSObject
@property (nonatomic, copy) NSString *name;
@end

@implementation Person

@end
Copy the code

2. Check the memory usage:

Note the need to introduce #import

Class_getInstanceSize is used to get the memory size of the instance object of the class and return the specific number of bytes. It is essentially to get the memory size of the member variables in the instance object

3. Add attributes

We add the nickName property to Person, assign it, and see the print again

Continue adding the height member variable to Person

@interface Person : NSObject {
    int height;
}
@property (nonatomic, copy) NSString *name;
@property (nonatomic, copy) NSString *nickName;
@end
Copy the code

View the print

4. Add methods

Add the – (void)work and + (void)say methods to Person and implement them

@interface Person : NSObject {
    int height;
}
@property (nonatomic, copy) NSString *name;
@property (nonatomic, copy) NSString *nickName;
- (void)work;
+ (void)say;
@end

@implementation Person
- (void)work {
}

+ (void)say {   
}
@end
Copy the code

To view the print:

5, conclusion

As you can see from the previous steps, properties and member variables can affect the object memory. Methods do not affect the object memory

Final conclusion: The factor that affects the memory of object is member variable

There is internal alignment of structures

Table of bytes occupied by type

C OC 32 – A 64 – bit
bool BOOL(64位) 1 1
signed char (__signed char)int8_t, BOOL(32) bits 1 1
unsigned char Boolean 1 1
short int16_t 2 2
unsigned short unichar 2 2
int int32_t NSInteger(32-bit), boolean_t(32-bit) 4 4
unsigned int Boolean_t (64 bits), NSUInteger(32 bits) 4 4
long NSInteger(64位) 4 8
unsigned long NSUInteger(64位) 4 8
long long int64_t 8 8
float CGFloat(32位) 4 4
double CGFloat(64位) 8 8

Three principles of memory alignment

1. Data member alignment rules

The first member of a struct(or union) is stored at offset 0, and each subsequent data member is stored starting with an integer multiple of the size of the member or the size of its children (as long as the member has children, such as arrays, structs, etc.) (int is 4 bytes). Start with an integer multiple of 4.)

2. Structure as a member

If a structure has any struct members, the struct members should be stored from the address of the largest internal element multiple (struct A contains struct b, and b contains char, int, double, etc., then b should be stored from the address of multiple 8), because double takes 8 bytes

3, the total size of the structure

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

Here’s an example:

struct Struct1 { double a; [0 7] char b; [8] int c; [8] int c; // The next index 9 is not an integer multiple of 4, so 9, 10, 11 is null and stored in [12 13 14 15] short d; // The next index 16 is an integer multiple of 2, stored in [16 17]}struct1; Struct Struct2 {double a; // struct {double a; [0 7] int b; // the next index 8 is an integer multiple of 4, stored in [8 9 10 11] char c; [12] short d; [12] short d; // The next index 13 is not an integer multiple of 2, so empty 13 stored [14 15]}struct2; // Struct Struct3 {double a; // Struct {double a; [0 7] int b; // the next index 8 is an integer multiple of 4, stored in [8 9 10 11] char c; [12] short d; [12] short d; // The next index 13 is not an integer multiple of 2, so empty 13 and store [14 15] int e; [16 17 18 19] struct Struct1 STR; [24.....46]}struct3; // The next index is not a double 8-byte multiple in STR, so empty 20, 21, 22, 23, stored in [24.....46]}struct3; // The total interval is [0...46], the size is 47, take the integer multiple of the largest element double8 bytes, So the total size is 48 NSLog(@"\n first struct size :%lu\n Second struct size :%lu\n Third struct size :%lu", sizeof(struct1), sizeof(struct2), sizeof(struct3));Copy the code

Final print result:

First structure size :24 Second structure size :16 Third structure size :48Copy the code

Add substruct to Struct3:

struct Struct1 { double a; [0 7] char b; [8] int c; [8] int c; // The next index 9 is not an integer multiple of 4, so 9, 10, 11 is null and stored in [12 13 14 15] short d; // The next index 16 is an integer multiple of 2, stored in [16 17]}struct1; Struct Struct2 {double a; // struct {double a; [0 7] int b; // Account for 4 bytes next index 8 is an integer multiple of 4, stored in [8 9 10 11] float c; // The next index 12 is an integer multiple of 4, stored in [12 13 14 15] short d; // The next index 16 is an integer multiple of 2, stored in [16 17] bool e; [18]}struct2; Struct {double a; // struct {double a; [0 7] int b; // the next index 8 is an integer multiple of 4, stored in [8 9 10 11] char c; // The next index 12 is an integer multiple of 1, stored in [12] short d; [14 15] int e; [14 15] int e; [14 15] int e; [16 17 18 19] struct Struct1 str1; [24.....47] struct t2 str2; // struct t2 str2; // The next index 48 is a double of 8 bytes in str2, stored in [48.....71] bool f; // The next index 48 is a double of 8 bytes in STR2, stored in [48.....71] bool F; // The next index 72 is an integer multiple of 1, stored in [72]}struct3; // The total interval is [0...72] and the size is 73. Take the integer multiple of the largest element double8 bytes, so the total size is 80Copy the code

mallocThe source code to introduce

Sizeof, class_getInstanceSize, and malloc_SIZE

1. We willPersonThe class is modified as follows:

@interface Person : NSObject
@property (nonatomic, copy) NSString *name;
@property (nonatomic, copy) NSString *nickName;
@property (nonatomic, assign) int age;
@property (nonatomic, assign) long height;
@end

@implementation Person

@end
Copy the code

2, and then atmainIn the code:

Need to introduce #import

and #import

int main(int argc, char * argv[]) { NSString * appDelegateClassName; @autoreleasepool { Person *person = [Person alloc]; Person. Name = @" @3 "; Person. nickName = @" nickName "; NSLog(@"person==>%@", person); NSLog(@"sizeof==>%lu", sizeof(person)); NSLog(@"class_getInstanceSize==%zu", class_getInstanceSize([Person class])); NSLog(@"malloc_size==%zu", malloc_size((__bridge const void *)(person))); appDelegateClassName = NSStringFromClass([AppDelegate class]); } return UIApplicationMain(argc, argv, nil, appDelegateClassName); }Copy the code

3. Print results:

4. Explanation:

  • sizeofCalculates the memory size of data (arrays, variables, types, structures, etc.) in unitsbyte
    • personIs an object whose struct pointer address is8byte
  • class_getInstanceSizeNote how much memory space is used to calculate objects and member variablesThe parent class attributeandisa.8Byte alignment
    • name(NSString8 bytes) +nickName(NSString8 bytes) +age(int4 bytes) +height(long8 bytes) +isa(from NSObject8 bytes) =36Bytes, according to8Byte alignment rules, and finally40byte
  • malloc_sizeCalculate the memory space actually requested from the system,16Byte alignment
    • 40When a byte applies to the system, follow16Byte alignment principle, and finally48byte

So malloc is how to apply for memory system, we next combined with malloc source analysis

mallocSource code analysis

1, positioningmalloc_sizeSource location

Click malloc_size in the project to locate its location in the source code as follows:

Then we can download the source code for malloc to analyze

Malloc source download address, we take the source code version 317.40.8 as an example for analysis

2,mallocSource code analysis

We analyze the source code in the context of the above conclusion that the actual 40 bytes are used and the final 48 bytes are applied

  • Application for space

We add the following code to the main method of the malloc source code:

As we know from printing, eventually it did48Byte space, so how does that work? Below we combine source code analysis

  • Calling process

By analyzing the source code, we find the final entry method of calloc->_malloc_zone_calloc

According to our analysis, the core code isptr = zone->calloc(zone, num_items, size);But we can’t see itzone->callocThe concrete implementation of, here we have two ways to continue the analysis:

1. Print through the Po

2. Assemble by assembly

Open the assembly window, control+step into all the way down the execution, finally enter

We searched default_zone_calloc globally, added a breakpoint, and closed the assembly window to continue. The result is as follows:

We Po print discovery, and then nano_calloc method of nano_malloc.c is called

Go to the nano_malloc method, hit a breakpoint, and continue executing:

Slot_LIFO –Last in, First out indicates that the memory is open on the heap

There are two known algorithms for 16-byte alignment:

  • allocIn the sourcealign16
  • mallocIn the sourcesegregated_size_to_fit

In the _nano_malloc_check_clear method, we can clearly see a call to the segregated_size_to_fit alignment algorithm. Click inside the segregated_size_to_fit method:

Typical alignment algorithm, 40 is aligned to 48 bytes after 16 bytes, and this is the core that will eventually open up 48 bytes of space

conclusion

  • The heapOn theobjectMemory to16Byte alignment
  • The object’sMember variablesMemory to8Byte alignment
  • objectwithobjectBetween memory and16Byte alignment