對象內存對齊
探討的問題
1.什么是內存對齊?
2.為什么要做內存對齊?
3.結構體內存對齊規則
4.源碼內存對齊算法
一.什么是內存對齊?
計算機內存都是以字節為單位劃分的,從理論上講似乎對任何類型的變量的訪問可以從任何地址開始,但是實際的計算機系統對基本類型數據在內存中存放的位置有限制,它們會要求這些數據的首地址的值是某個數k(通常它為4或8的倍數),這就是所謂的內存對齊。內存對齊是一種在計算機內存中排列數據(表現為變量的地址) 、訪問數據(表現為CPU讀取數據)的一種方式。
內存對齊包含了兩種相互獨立又相互關聯的部分:基本數據對齊和結構體數據對齊 。
二.為什么要做內存對齊?
1.平臺原因(移植原因):不是所有的硬件平臺都能訪問任意地址上的任意數據的;某些硬件平臺只能在某些地址處取某些特定類型的數據,否則拋出硬件異常。
2.性能原因:數據結構(尤其是棧)應該盡可能地在自然邊界上對齊。原因在于,為了訪問未對齊的內存,處理器需要作兩次內存訪問;而對齊的內存訪問僅需要一次訪問。
三.結構體內存對齊規則
當我們定義一個 struct 的時候,它在內存中是怎么存儲的?占用了多少字節的內存空間呢?
//1、定義兩個結構體
struct Mystruct1{
char a; //1字節
double b; //8字節
int c; //4字節
short d; //2字節
}Mystruct1;
struct Mystruct2{
double b; //8字節
int c; //4字節
short d; //2字節
char a; //1字節
}Mystruct2;
//計算 結構體占用的內存大小
NSLog(@"%lu-%lu",sizeof(Mystruct1),sizeof(Mystruct2));
輸出 24-16
從打印結果看出一個問題,結構體中變量相同,只是順序不同,結果影響結構體占用內存大小,這就是iOS中內存對齊
內存對齊規則
1.數據成員對?規則:結構(struct)(或聯合(union))的數據成員,第
一個數據成員放在offset為0的地方,以后每個數據成員存儲的起始位置要
從該成員大小或者成員的子成員大小(只要該成員有子成員,比如說是數組,
結構體等)的整數倍開始(比如int為4字節,則要從4的整數倍地址開始存
儲。 min(當前開始的位置m,n) m=9 n=4 9 10 11 12
2.結構體作為成員:如果一個結構里有某些結構體成員,則結構體成員要從
其內部最大元素大小的整數倍地址開始存儲.(struct a里存有struct b,b
里有char,int ,double等元素,那b應該從8的整數倍開始存儲.)
3.收尾工作:結構體的總大小,也就是sizeof的結果,.必須是其內部最大
成員的整數倍.不足的要補?。
拿上面的兩個結構體舉例說明
struct Mystruct1{
char a; //1字節 從 0開始 1結束 min(0,1)
double b; //8字節 從 1開始 1不能整除8 從8開始 8到15 min(8,8)
int c; //4字節 從 16開始 能整除4 16到19
short d; //2字節 從 20開始 20能整除2 20到21
}Mystruct1;
最后 Mystruct1 中變量最大字節是8 Mystruct1 0-21 共22字節 22向上取整8的倍數 = 24, sizeof(Mystruct1)=24
struct Mystruct3{
int a; //4字節 min(0,4)--- (0,1,2,3)
struct Mystruct4{
//從4開始,存儲開始位置必須是最大的整數倍(最大成員為8),min(4,8)不滿足, min(8,8)滿 足,從8開始存儲
double b; //8字節 min(8,8) --- (8,9,10,11,12,13,14,15)
short c; //2字節,從16開始,min(6,2) -- (16,17,18)
char d; //1字節,從19開始,min(19,1) -- (19)
}Mystruct4;
}Mystruct3;
Mystruct3 內存大小必須是 Mystruct4 中變量最大字節數即8的倍數 Mystruct3 0-19 共20個字節 20向上取整8的倍數=24
打印內存大小方法
sizeof 最終得到的結果是該數據類型占用空間的大小
class_getInstanceSize 獲取類的實例對象所占用的內存大小
malloc_size 獲取系統實際分配的內存大小
int main(int argc, const char * argv[]) {
@autoreleasepool {
NSObject *objc = [[NSObject alloc] init];
NSLog(@"objc對象類型占用的內存大小:%lu",sizeof(objc));
NSLog(@"objc對象實際占用的內存大小:%lu",class_getInstanceSize([objc class]));
NSLog(@"objc對象實際分配的內存大小:%lu",malloc_size((__bridge const void*)(objc)));
}
return 0;
}
打印 8 8 16
內存對齊算法
align16: 16字節對齊算法
static inline size_t align16(size_t x) {
return (x + size_t(15)) & ~size_t(15);
}
#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;
}
算法原理:k + 15 >> 4 << 4 ,其中 右移4 + 左移4相當于將后4位抹零,跟 k/16 * 16一樣 ,是16字節對齊算法,小于16就成0了
對于一個對象來說,其真正的對齊方式 是 8字節對齊,8字節對齊已經足夠滿足對象的需求了
apple系統為了防止一切的容錯,采用的是16字節對齊的內存,主要是因為采用8字節對齊時,兩個對象的內存會緊挨著,顯得比較緊湊,而16字節比較寬松,利于蘋果以后的擴展。