Internal alignment of the structure
Memory alignment principles
-
The data member of a struct (or union), the first data member 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 (as long as the member has children, such as arrays). Structure, etc.) (for example, if int is 4 bytes, it is stored from the address that is a multiple of 4.
-
Struct as members: If a structure has some struct members, the structure members are stored from an integer multiple of the size of the largest element in the structure.
-
Wrap up: the total sizeof a structure, the result of sizeof, must be an integer multiple of its largest internal member. What is lacking must be made up.
struct WLWStruct1 {
double a; / / 8 7 [0]
char b; / / 1 [8]
int c; // 4 (9 10 11 [12 13 14 15]
short d; // 2 [16 17] 24
}struct1;
struct WLWStruct2 {
double a; / / 8 7 [0]
int b; // 4 [8 9 10 11]
char c; / / 1 [12]
short d; // 2 (13 [14 15] 16
}struct2;
Copy the code
Struct1 and Struct2 have the same internal members, but they are not arranged in the same order
Complex structures
struct WLWStruct3 {
double a; / / 8 7 [0]
int b; // 4 [8 9 10 11]
char c; / / 1 [12]
short d; // 2 (13 [14 15]
int e; // 4 [16 17 18 19] 24
struct WLWStruct1 str; 24
}struct3; 48
Copy the code
The structure is nested, and the internal structure must conform to the memory alignment principle, as shown below
NSLog(@"struct1--%lu \n struct2--%lu \n struct3--%lu",sizeof(struct1),sizeof(struct2),sizeof(struct3)); 2021-06-21 18:28:30.185599+0800 001- memory alignment principle [10150:4530680] struct1--24 struct2--16 struct3--48Copy the code
Why align
In order to improve the speed of reading memory, it only needs to follow a fixed offset, and does not need to consider the size of the next piece of memory. In fact, it is a strategy of trading space for time
Factors affecting object memory
We all know that a class has member variables, properties, methods, protocols. When you initialize an object, how does the size of the object affect it? In the underlying alloc process, we found an important method
static ALWAYS_INLINE id
_class_createInstanceFromZone(Class cls, size_t extraBytes, void *zone,
int construct_flags = OBJECT_CONSTRUCT_NONE,
bool cxxConstruct = true.size_t *outAllocatedSize = nil)
{
// CLS = initialized class: WLWPerson
// extraBytes = 0
// zone = nil
// construct_flags = OBJECT_CONSTRUCT_CALL_BADALLOC
ASSERT(cls->isRealized());
// Read class's info bits all at once for performance
// Reading all information about a class at once improves performance
bool hasCxxCtor = cxxConstruct && cls->hasCxxCtor();
bool hasCxxDtor = cls->hasCxxDtor();
bool fast = cls->canAllocNonpointer();
size_tsize; size = cls->instanceSize(extraBytes); . . }Copy the code
ALWAYS_INLINE
ALWAYS_INLINE
Corresponding macro definition#define ALWAYS_INLINE inline __attribute__((always_inline))
Is force-inlining a function, which means that the code inside a function is inserted directly into the place of the call, without the compiler compiling it into a function call
ALWAYS_INLINE int a(a) {}int b(a) {
a()
}
Copy the code
- Assembly code does not jump to A, but instead is directly part of b inside a function, which is faster
size = cls->instanceSize(extraBytes)
inline size_t instanceSize(size_t extraBytes) const {
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;
}
size_t fastInstanceSize(size_t extra) const
{
ASSERT(hasFastInstanceSize(extra));
// __builtin_constant_p() returns 1 if the value of x can be determined at compile time
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
returnalign16(size + extra - FAST_CACHE_ALLOC_DELTA16); }}Copy the code
- We found that this function is used to calculate the size of memory,
inline
Inline function fastpath(cache.hasFastInstanceSize(extraBytes))
It tells us that there’s a high probability of cachingcache.fastInstanceSize(extraBytes)
_flags & FAST_CACHE_ALLOC_MASK
This is taking the size of the cachealign16(size + extra - FAST_CACHE_ALLOC_DELTA16)
16-byte alignment- algorithm
(x + size_t(15)) & ~size_t(15)
That’s the last four zeroes, which is a multiple of 16
Here's an example: 5 + 15&~ 15 = 20&~ 15 binary 20 = 0001 0100 15 = 0000 1111 ~15 = 1111 0000 20&~ 15 = 0001 0100 = 0001 0000 = 16 1111 0000Copy the code
alignedInstanceSize()
// May be unaligned depending on class's ivars. uint32_t unalignedInstanceSize(a) const { ASSERT(isRealized()); return data()->ro()->instanceSize; } // Class's ivar size rounded up to a pointer-size boundary. uint32_t alignedInstanceSize(a) const { return word_align(unalignedInstanceSize()); } static inline uint32_t word_align(uint32_t x) { return (x + WORD_MASK) & ~WORD_MASK; } define WORD_MASK 7UL Copy the code
- You can see that there is a comment that the size depends only on the member variable
class's ivars
- That is
data()->ro()->instanceSize
The size of theclean memory
- Out of cache
(x + 7) & ~7
The algorithm is aligned with 16 bytes, and the last three digits erase zero, which is a multiple of 8
- You can see that there is a comment that the size depends only on the member variable
(size < 16) size = 16
Minimum 16 bytes
Why 8-byte alignment or 16-byte alignment
- To make it easy to read,
- We all know that the system is 64-bit, so it’s safer to read in multiples of 8, which effectively avoids reading into other memory
- Fault-tolerant processing
- Space for time
Object size
@interface WLWPerson: NSObject {// isa // 8 // 8 } @property (nonatomic, copy) NSString *nickName; // 8 @property (nonatomic, assign) int age; // 4 @property (nonatomic) double height; // 8 @property (nonatomic) char c1; // 1 @property (nonatomic) char c2; // 1 - (void)sayNo; + (void)sayYes; @end WLWPerson *person = [WLWPerson alloc]; NSLog(@" memory sizeof object pointer --%lu",sizeof(person)); NSLog(@" object actual memory size <8 bytes aligned >--%lu",class_getInstanceSize([person class])); NSLog(@" system allocated memory size <16 bytes aligned >--%lu",malloc_size((__bridge const void *)(person)));Copy the code
2021-06-22 15:36:59.650785+0800 001- Memory alignment rule [13780:4765193] Object pointer memory size --8 2021-06-22 15:36:59.651364+0800 Actual memory size <8 byte alignment >--40 2021-06-22 15:36:59.651499+0800 001- Memory alignment principle [13780:4765193] System-allocated memory size <16 byte alignment >--48Copy the code
- The system will optimize the underlying structure, such as two
int
Types exist together because of oneint
It’s 4 bytes. Two bytes is exactly 8 bytes to avoid waste
Malloc analysis explores ideas
obj = (id)calloc(1, size)
Is used to open up memory for objects, but we can’t see the implementation in the Runtime source code, so we need to introduce itmalloc
Source code to find out, inlibmalloc
In the source
int main(int argc, Const char * argv[]) {@autoreleasepool {void *p = calloc(1, 40); const char * argv[]) {@autoreleasepool {void *p = calloc(1, 40); NSLog(@"%lu",malloc_size(p)); NSLog(@"Hello, World!" ); } return 0; }Copy the code
Compile the print
- Found that the call is
default_zone_calloc
- Byte alignment algorithm
size + 16 - 1 >> 4 << 4
Is an integer multiple of 16 calloc
Open up memory is 16 byte aligned
Why member variables are internally 8-byte aligned while objects are 16-byte aligned,
- Can prevent wild pointer access, access to the memory of other objects, resulting in access errors
- For example, if the object is 24 bytes and aligned with 16 bytes, it will be free, more secure and more fault tolerant