Welcome to the iOS Exploration series.
- IOS explores the alloc process
- IOS explores memory alignment &malloc source code
- IOS explores ISA initialization & pointing analysis
- IOS exploration class structure analysis
- IOS explores cache_T analysis
- IOS explores the nature of methods and the method finding process
- IOS explores dynamic method resolution and message forwarding mechanisms
- IOS explores the dyLD loading process briefly
- The loading process of iOS explore classes
- IOS explores the loading process of classification and class extension
- IOS explores isa interview question analysis
- IOS Explore Runtime interview question analysis
- IOS explores KVC principles and customization
- IOS explores KVO principles and customizations
- IOS explores the principle of multithreading
- IOS explores multi-threaded GCD applications
- IOS explores multithreaded GCD underlying analysis
- IOS explores NSOperation for multithreading
- IOS explores multi-threaded interview question analysis
- IOS Explore the locks in iOS
- IOS explores the full range of blocks to read
Writing in the front
In iOS explore alloc flow, the process of creating the underlying object is described, so this article will explore the arrangement of attributes in the object in memory
1. Explore objectives
1. Test code
#import <Foundation/Foundation.h> #import <objc/runtime.h> #import <malloc/malloc.h> @interface FXPerson : NSObject @property (nonatomic, assign) NSInteger age; @property (nonatomic, copy) NSString *name; @property (nonatomic, assign) long height; @property (nonatomic, assign) char c1; @property (nonatomic, assign) char c2; @end @implementation FXPerson @end int main(int argc, const char * argv[]) { @autoreleasepool { FXPerson *p = [FXPerson alloc]; p.name = @"Felix"; p.age = 20; p.height = 180; p.c1 = 'a'; p.c2 = 'b'; NSLog (@ "\ nsizeof - % lu lu \ \ nclass_getInstanceSize - % nmalloc_size - % lu", sizeof ([p class]), class_getInstanceSize([p class]), malloc_size((__bridge const void *)(p))); } return 0; }Copy the code
** Note: If the object is created with no assigned attribute — it will be a memory dummy address
2. Prepare knowledge of LLDB debugging commands
(1) The x object prints the memory address of the object in hexadecimal (x indicates hexadecimal).
Because iOS is in small-endian mode (high bytes of data are stored at high addresses in memory, and low bytes of data are stored at low addresses in memory — the data is stored in reverse), data is read backwards
(lldb) x p 0x10060eea0: c5 13 00 00 01 80 1d 00 61 62 00 00 00 00 00 00 ........ ab...... 0x10060eeb0: 14 00 00 00 00 00 00 00 50 10 00 00 01 00 00 00 ........ P.......Copy the code
② X /4gx object represents the output of four hexadecimal 8-byte address space (x represents the hexadecimal, 4 represents the four, g represents the unit of 8 bytes, the same as x/4xg object)
(lldb) x/4gx p
0x10060eea0: 0x001d8001000013c5 0x0000000000006261
0x10060eeb0: 0x0000000000000014 0x0000000100001050
Copy the code
The memory address is on the left and the memory value is on the right
③ Po and p: p stands for “expression” — prints the object pointer; And Po is “expression-o” — prints the object itself
(lldb) p p
(FXPerson *) $0 = 0x0000000101857750
(lldb) po p
<FXPerson: 0x101857750>
Copy the code
Check the memory address debug->Debug Workflow-> View Memory
Some operations may not be needed, so the reader is free to expand
3. Modify the code to view the memory
The FXPerson class declares name first, followed by age
(lldb) x/6gx p
0x10062c380: 0x001d8001000013c5 0x0000000000006261
0x10062c390: 0x0000000100001050 0x0000000000000014
0x10062c3a0: 0x00000000000000b4 0x0000000000000000
Copy the code
The FXPerson class declares age and then name
(lldb) x/6gx p
0x100538e00: 0x001d8001000013c5 0x0000000000006261
0x100538e10: 0x0000000000000014 0x0000000100001050
0x100538e20: 0x00000000000000b4 0x0000000000000000
Copy the code
According to our computer foundation and LLDB instructions, it can be found
- I don’t know what the first paragraph is
- In the second paragraph, 62 and 63 are respectively
a
,b
ASCII code - 14 in paragraph 3 is
20
In hexadecimal - In paragraph 4, the Po appears to be
Felix
- The fifth section is
180
4. View the console output
Sizeof - 8 class_getInstanceSize - 40 malloc_size - 48Copy the code
5. Remove the declared properties and view the console output
No attributes are declared in the FXPerson class
Sizeof - 8 class_getInstanceSize - 8 malloc_size - 16Copy the code
6. Ask questions
Q1: Why does the declaration of attributes affect the memory arrangement of objects?
Q2: What are sizeof, class_getInstanceSize, and malloc_size?
Q3: Instead of saying that the object is at least 16 bytes, why does class_getInstanceSize output 8 bytes?
Second, memory alignment
1. Binary rearrangement
Binary rearrangement – Aggregates together the code that is most frequently executed or that needs the most critical execution (such as sequential calls to the startup phase), and places the non-essential code in a lower priority to form a more compact __TEXT segment
2. Memory optimization
If the objects are allocated in the order in which they are declared by default, a lot of memory is wasted on 8-byte alignment of attributes, so the system rearranges the objects’ attributes to maximize our memory space — much like binary rearrangement
Sizeof, class_getInstanceSize, and malloc_size
Sizeof: an operator that gets the sizeof the type’s memory at compile time
Class_getInstanceSize: returns the memory required to create an instance object, depending on
Malloc_size: returns the actual memory size allocated by the system, depending on
Another 8 bytes are output for class_getInstanceSize
size_t class_getInstanceSize(Class cls)
{
if(! cls)return 0;
return cls->alignedInstanceSize();
}
Copy the code
Why does class_getInstanceSize return 8 bytes, which is a mistake !!!! If you search the objC source for class_getInstanceSize, you’ll find that it only does byte alignment — 8-byte alignment, Alloc code of at least 16 bytes is not included in class_getInstanceSize call stack — if (size < 16) size = 16;
4. Align memory
The properties of an object are memory-aligned, and the object itself is memory-aligned
- Data member alignment principle: the first data member of a struct (or union) is placed at offset 0, and the starting position of each data member is from the size of the member or the size of the member’s children
- Struct as members: If a structure has some struct members, the struct members are stored from an integer multiple of the size of the largest element in the structure
- Wrap up: the total sizeof the structure, the result of sizeof, must be an integer multiple of the largest member within the structure
5. Give me an example
struct struct1 { char a; double b; int c; short d; } str1; struct struct2 { double b; char a; int c; short d; } str2; struct struct3 { double b; int c; char a; short d; } str3; Int main(int argc, const char * argv[]) {@autoreleasepool {NSLog(@"%lu -- %lu -- %lu", sizeof(str1), sizeof(str2), sizeof(str3)); } return 0; }Copy the code
The output is 24-24-16
We know that (64-bit) char is 1 byte, double is 8 bytes, int is 4 bytes, and short is 2 bytes
The memory alignment principle can be simply understood as min(m,n) — m is the current starting position, and n is the number occupied. When m is an integer multiple of n, the condition is satisfied; Otherwise m bit is empty, m+1, continue min algorithm.
For example, b in str1, which starts with min(1,8), does not satisfy the condition until min(8,8), so it sits in the eighth place, occupying eight cells
For example, c in str2, which starts with min(9,4), does not satisfy the condition until min(12,4), so it sits in the 12th place, occupying four cells
For example, d in str3, which starts with min(13,2), does not satisfy the condition until min(14,2), so it sits in the 14th place, occupying two cells
Sohu public number of a push – memory layout (very detailed, recommended reading)
3. Malloc process
There is also a legacy issue with memory allocation — alloc calls obj = (id)calloc(1, size) when requesting underlying memory space. The objc source code was the only source we didn’t know where to start. Now we can use the libmalloc source code to find out
1.calloc
Create a new target in the libmalloc source code and call it as in the objc source code
void *p = calloc(1, 40);
Copy the code
2.malloc_zone_calloc
void *
calloc(size_t num_items, size_t size)
{
void *retval;
retval = malloc_zone_calloc(default_zone, num_items, size);
if (retval == NULL) {
errno = ENOMEM;
}
return retval;
}
Copy the code
Return retval retval is the core content, so check out malloc_zone_calloc
3 default_zone_calloc
void *
malloc_zone_calloc(malloc_zone_t *zone, size_t num_items, size_t size)
{
MALLOC_TRACE(TRACE_calloc | DBG_FUNC_START, (uintptr_t)zone, num_items, size, 0);
void *ptr;
if (malloc_check_start && (malloc_check_counter++ >= malloc_check_start)) {
internal_check();
}
ptr = zone->calloc(zone, num_items, size);
if (malloc_logger) {
malloc_logger(MALLOC_LOG_TYPE_ALLOCATE | MALLOC_LOG_TYPE_HAS_ZONE | MALLOC_LOG_TYPE_CLEARED, (uintptr_t)zone,
(uintptr_t)(num_items * size), 0, (uintptr_t)ptr, 0);
}
MALLOC_TRACE(TRACE_calloc | DBG_FUNC_END, (uintptr_t)zone, num_items, size, (uintptr_t)ptr);
return ptr;
}
Copy the code
PTR = zone->calloc(zone, num_items, size); Follow through and you’ll find a bewildering array of code
void *(* MALLOC_ZONE_FN_PTR(calloc))(struct _malloc_zone_t *zone, size_t num_items, size_t size); /* same as malloc, but block returned is set to zero */
Copy the code
3.1 Method one — Analyze the zone
If a zone is of type malloc_zone_t, retval = malloc_zone_calloc(default_zone, num_items, size); The first parameter passed in zone is again default_zone, which you can trace back to as a static variable
static malloc_zone_t *default_zone = &virtual_default_zone.malloc_zone;
Copy the code
static virtual_default_zone_t virtual_default_zone
__attribute__((section("__DATA,__v_zone")))
__attribute__((aligned(PAGE_MAX_SIZE))) = {
NULL.NULL,
default_zone_size,
default_zone_malloc,
default_zone_calloc,
default_zone_valloc,
default_zone_free,
default_zone_realloc,
default_zone_destroy,
DEFAULT_MALLOC_ZONE_STRING,
default_zone_batch_malloc,
default_zone_batch_free,
&default_zone_introspect,
10,
default_zone_memalign,
default_zone_free_definite_size,
default_zone_pressure_relief,
default_zone_malloc_claimed_address,
};
Copy the code
Zone ->alloc is default_zone_calloc
3.2 Method two — Console printing
Sometimes printing is a way to read the source code — it tells you that default_zone_calloc is actually called
3.3 the conclusion
As long as the mind does not slide, the method is more than difficult
static void *
default_zone_calloc(malloc_zone_t *zone, size_t num_items, size_t size)
{
zone = runtime_default_zone();
return zone->calloc(zone, num_items, size);
}
Copy the code
4.nano_malloc
Zone ->calloc(zone, num_items, size)
Let’s go ahead and print
5._nano_malloc_check_clear
nano_malloc(nanozone_t *nanozone, size_t size)
{
if (size <= NANO_MAX_SIZE) {
void *p = _nano_malloc_check_clear(nanozone, size, 0);
if (p) {
return p;
} else {
/* FALLTHROUGH to helper zone */}}malloc_zone_t *zone = (malloc_zone_t *)(nanozone->helper_zone);
return zone->malloc(zone, size);
}
Copy the code
Shift + command + O to nano_malloc
Analysis: /* FALLTHROUGH to helper zone */ FALLTHROUGH to helper zone */ That is, under normal circumstances go if judgment (if the space to be opened up is smaller than NANO_MAX_SIZE then nanozone_t malloc) NANO_MAX_SIZE=256
6.segregated_size_to_fit
static void *
_nano_malloc_check_clear(nanozone_t *nanozone, size_t size, boolean_t cleared_requested)
{
MALLOC_TRACE(TRACE_nano_malloc, (uintptr_t)nanozone, size, cleared_requested, 0);
void *ptr;
size_t slot_key;
size_t slot_bytes = segregated_size_to_fit(nanozone, size, &slot_key); // Note slot_key is set here
mag_index_t mag_index = nano_mag_index(nanozone);
nano_meta_admin_t pMeta = &(nanozone->meta_data[mag_index][slot_key]);
ptr = OSAtomicDequeue(&(pMeta->slot_LIFO), offsetof(struct chained_block_s, next));
if (ptr) {
...
} else {
ptr = segregated_next_block(nanozone, pMeta, slot_bytes, mag_index);
}
if (cleared_requested && ptr) {
memset(ptr, 0, slot_bytes); // TODO: Needs a memory barrier after memset to ensure zeroes land first?
}
return ptr;
}
Copy the code
Analysis: don’t panic when you see such a long code at the moment, if-else only one. If you think again, we’re looking at the source code with a purpose — where the 48 in malloc_size comes from. Size = 40; slot_bytes = 48; 40->48
7. 16-byte alignment
static MALLOC_INLINE size_t
segregated_size_to_fit(nanozone_t *nanozone, size_t size, size_t *pKey)
{
// size = 40
size_t k, slot_bytes;
if (0 == size) {
size = NANO_REGIME_QUANTA_SIZE; // Historical behavior
}
// 40 + 16-1 >> 4 << 4
// 40-16*3 = 48
//
/ / 16
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
Size = 40; after (40 + 16-1) >> 4 << 4, the result is 48, which is an integer multiple of 16
8. The malloc summary
The attribute of the object is 8-byte alignment
The object is 16-byte aligned
- Because memory is contiguous, 16-byte alignment avoids risk and fault tolerance and prevents access overflow
- At the same time, it also improves the efficiency of addressing access, i.e., space swap time
9. Flow chart of malLOC part
Cooci’s Malloc analysis
Write in the back
As for writing an article, I like to learn it systematically first and write down the overall outline, but I may have new insights in the follow-up study and update the article from time to time, but the main points of the article are correct