iOS之底層內(nèi)存對齊

引言

內(nèi)存對齊是內(nèi)存里面一個很重要的詞匯傅瞻,可是大部分開發(fā)者對這個詞匯的含義都是一知半解拧粪。

 WJPerson*wj = [WJPerson alloc];
wj.name = @"無極";
wj.age = 30;
NSLog(@"對象類型的內(nèi)存大小:%lu",sizeof(wj));
NSLog(@"對象實際的內(nèi)存大小:%lu",class_getInstanceSize([wj class]));
NSLog(@"對象分配的內(nèi)存大小:%lu",malloc_size((__bridge const void *)(wj)));
NSLog(@"-----------------------------------------");

WJPerson*wj2;
NSLog(@"對象類型的內(nèi)存大邪锓恰:%lu",sizeof(wj2));
NSLog(@"對象實際的內(nèi)存大小:%lu",class_getInstanceSize([wj2 class]));
NSLog(@"對象分配的內(nèi)存大卸锬ⅰ:%lu",malloc_size((__bridge const void *)(wj2)));

輸出結(jié)果:

2021-06-16 13:12:08.712404+0800 內(nèi)存對齊[3440:72350] 對象類型的內(nèi)存大心┛:8
2021-06-16 13:12:08.712537+0800 內(nèi)存對齊[3440:72350] 對象實際的內(nèi)存大小:24
2021-06-16 13:12:08.712659+0800 內(nèi)存對齊[3440:72350] 對象分配的內(nèi)存大凶俊:32
2021-06-16 13:12:08.712746+0800 內(nèi)存對齊[3440:72350] -----------------------------------------
2021-06-16 13:12:08.712831+0800 內(nèi)存對齊      [3440:72350] 對象類型的內(nèi)存大性刹铡:8
2021-06-16 13:12:08.712923+0800 內(nèi)存對齊[3440:72350] 對象實際的內(nèi)存大小:0
2021-06-16 13:12:08.713007+0800 內(nèi)存對齊[3440:72350] 對象分配的內(nèi)存大邪孀小:0

結(jié)果分析:

  • sizeof:對象類型的內(nèi)存大小隅忿,sizeof是用來計算一個變量或者一個常量心剥、一種數(shù)據(jù)類型所占的內(nèi)存字節(jié)數(shù)。自定義對象的本質(zhì)是結(jié)構(gòu)體指針背桐,所以占8個字節(jié)。
  • class_getInstanceSize:對象實際(對齊后)的內(nèi)存大小蝉揍,內(nèi)存大小是由類的成員變量的大小決定的链峭。實際上并不是嚴格意義上的對象的內(nèi)存的大小,因為內(nèi)存進行了8字節(jié)對齊又沾,所以wj的內(nèi)存大小是24而不是20弊仪。而wj2只是聲明變量,并沒有走alloc方法開辟內(nèi)存杖刷,所以大小是0励饵。核心內(nèi)存大小算法是:define WORD_MASK 7UL ((x + WORD_MASK) & ~WORD_MASK
  • malloc_size:系統(tǒng)實際分配的內(nèi)存大小,以16字節(jié)對齊滑燃,不足16的自動補齊役听。注意:系統(tǒng)的16字節(jié)對齊是在實際的內(nèi)存大小(經(jīng)過8字節(jié)對齊后)的基礎(chǔ)上表窘。上面的wj對象實際內(nèi)存大小24字節(jié)典予,不是16的倍數(shù),所以系統(tǒng)實際分配為32乐严。

問題:class_getInstanceSizemalloc_size 底層做了什么瘤袖?我們?nèi)绾沃?code>class_getInstanceSize是8字節(jié)對齊,而malloc_size16字節(jié)對齊昂验?

在研究后面重點之前捂敌,我們先來看下基本數(shù)據(jù)類型在arm64環(huán)境下占用的內(nèi)存大小。

基本數(shù)據(jù)類型所占字節(jié)數(shù).gif

下面解釋為什么計算機會有內(nèi)存對齊的概念既琴,出于什么目的要內(nèi)存對齊占婉。

  • 內(nèi)存是以字節(jié)為基本單位,cpu在讀取數(shù)據(jù)時呛梆,是以為單位讀取锐涯,并不是以字節(jié)為單位讀取。頻繁讀取未對齊的數(shù)據(jù)填物,會加大cpu的開銷纹腌。字節(jié)對齊后,會降低cpu的存取次數(shù)滞磺,這種以空間時間的做法降低了cpu的開銷升薯。
  • cpu存取:是以為單位击困,存取未對齊的數(shù)據(jù)可能開始在上一個內(nèi)存塊涎劈,結(jié)束在另一個內(nèi)存塊广凸。這樣中間可能要經(jīng)過復(fù)雜的運算在合并在一起,降低了效率蛛枚,字節(jié)對齊后谅海,提高了cpu的訪問效率。

內(nèi)存對齊規(guī)則:

數(shù)據(jù)成員對齊規(guī)則:結(jié)構(gòu)體(struct)(或聯(lián)合體(union))的數(shù)據(jù)成員蹦浦,第一個數(shù)據(jù)成員放在offset為0的地方(即首地址的位置)扭吁,以后每個數(shù)據(jù)成員存儲的起始位置要從該成員大小或者成員的子成員大小(只要該成員有子成員盲镶,比如說是數(shù)組侥袜,結(jié)構(gòu)體等)的整數(shù)倍開始(比如int4字節(jié)),則要從4的整數(shù)倍地址開始存儲溉贿。
結(jié)構(gòu)體作為成員變量:如果一個結(jié)構(gòu)體里有某些結(jié)構(gòu)體成員枫吧,則結(jié)構(gòu)體成員要從其內(nèi)部最大元素大小的整數(shù)倍開始存儲(struct a里有struct b,b里有char宇色,int九杂,double等元素,那b應(yīng)該從8(doudle為8 )整數(shù)倍開始存儲)

下面我們先來看一個例子:


struct內(nèi)存.gif
struct WJPerson1{
double a;       
char b;       
int c;          
short d;
}myPerson1;
struct WJPerson2
{
double b;
int c;
char a;
short d;
}myPerson2;
NSLog(@"%lu-%lu",sizeof(myPerson1),sizeof(myPerson2));

輸出結(jié)果:

 2021-06-16 16:13:21.621334+0800 內(nèi)存對齊[4074:158189] 24-16

從上面我們可以看出代兵,myPerson1myPerson2兩個結(jié)構(gòu)體里面元素是一樣的尼酿,只是順序不同,內(nèi)存大小卻不一樣植影,為什么裳擎?這就是結(jié)構(gòu)體內(nèi)存對齊。

具體分析如下:[p,q] p表示當前開始的位置思币,q表示大小
myPerson1:

  • double a: [0,7] 即(0~7存放a)
  • char b:[8,1] 即(8存放b)
  • int c:[12,15] 即(9,10,11不是int 4得出倍數(shù)鹿响,廢棄,12~15存放c)
  • short d:[16,2] 即(16 ~ 17存放d)

myPerson2:

  • double a: (0,7) 即(0~7存放a)
  • int b:(8,4) 即(8~11存放b)
  • char c:(12,1) 即(12存放c)
  • short d:(14,2) 即(13不是d( short 2的倍數(shù))位置廢棄谷饿,14 ~ 15存放d)

下面這個是嵌套的結(jié)構(gòu)體
struct WJPerson3 {
double a;
int b;
char c;
short d;
int e;
struct WJPerson1 str;
} myPerson3;


struct嵌套內(nèi)存.gif

myPerson3具體分析如下:

  • double a: [0,7] 即(0~7存放a)
  • char b:[8,1] 即(8存放b)
  • int c:[12,15] 即(9,10,11不是int 4得出倍數(shù)惶我,廢棄,12~15存放c)
  • short d:[16,2] 即(16 ~ 17存放d)
  • int e:(20,4) 即(18,19不是int 4得出倍數(shù)博投,廢棄.20 ~ 23存放d)
  • 變量str: str是結(jié)構(gòu)體變量绸贡,內(nèi)存對齊原則結(jié)構(gòu)體成員要從其內(nèi)部最大元素大小的整數(shù)倍地址開始存儲。WJPerson1 中的最大的變量a( double)占8字節(jié)毅哗,所以offset從24開始听怕,WJPerson1的內(nèi)存大小是18字節(jié)。[24虑绵,18]尿瞭,即24 ~ 42存放 str,計算出來的是42個字節(jié)翅睛,但是myPerson3中最大的變量是str和 a都是 8 字節(jié)声搁,所以myPerson3的實際內(nèi)存大小必須是8的整數(shù)倍黑竞,42不是8的整數(shù)倍,因此補齊應(yīng)該是48.
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末疏旨,一起剝皮案震驚了整個濱河市很魂,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌充石,老刑警劉巖莫换,帶你破解...
    沈念sama閱讀 210,835評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異骤铃,居然都是意外死亡,警方通過查閱死者的電腦和手機坷剧,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,900評論 2 383
  • 文/潘曉璐 我一進店門惰爬,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人惫企,你說我怎么就攤上這事撕瞧。” “怎么了狞尔?”我有些...
    開封第一講書人閱讀 156,481評論 0 345
  • 文/不壞的土叔 我叫張陵丛版,是天一觀的道長。 經(jīng)常有香客問我偏序,道長页畦,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,303評論 1 282
  • 正文 為了忘掉前任研儒,我火速辦了婚禮豫缨,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘端朵。我一直安慰自己好芭,他們只是感情好,可當我...
    茶點故事閱讀 65,375評論 5 384
  • 文/花漫 我一把揭開白布冲呢。 她就那樣靜靜地躺著舍败,像睡著了一般。 火紅的嫁衣襯著肌膚如雪敬拓。 梳的紋絲不亂的頭發(fā)上邻薯,一...
    開封第一講書人閱讀 49,729評論 1 289
  • 那天,我揣著相機與錄音恩尾,去河邊找鬼弛说。 笑死,一個胖子當著我的面吹牛翰意,可吹牛的內(nèi)容都是我干的木人。 我是一名探鬼主播信柿,決...
    沈念sama閱讀 38,877評論 3 404
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼醒第!你這毒婦竟也來了渔嚷?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,633評論 0 266
  • 序言:老撾萬榮一對情侶失蹤稠曼,失蹤者是張志新(化名)和其女友劉穎形病,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體霞幅,經(jīng)...
    沈念sama閱讀 44,088評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡漠吻,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,443評論 2 326
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了司恳。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片途乃。...
    茶點故事閱讀 38,563評論 1 339
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖扔傅,靈堂內(nèi)的尸體忽然破棺而出耍共,到底是詐尸還是另有隱情,我是刑警寧澤猎塞,帶...
    沈念sama閱讀 34,251評論 4 328
  • 正文 年R本政府宣布试读,位于F島的核電站,受9級特大地震影響荠耽,放射性物質(zhì)發(fā)生泄漏钩骇。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,827評論 3 312
  • 文/蒙蒙 一骇塘、第九天 我趴在偏房一處隱蔽的房頂上張望伊履。 院中可真熱鬧,春花似錦款违、人聲如沸唐瀑。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,712評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽哄辣。三九已至,卻和暖如春赠尾,著一層夾襖步出監(jiān)牢的瞬間力穗,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,943評論 1 264
  • 我被黑心中介騙來泰國打工气嫁, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留当窗,地道東北人。 一個月前我還...
    沈念sama閱讀 46,240評論 2 360
  • 正文 我出身青樓寸宵,卻偏偏與公主長得像崖面,于是被迫代替她去往敵國和親元咙。 傳聞我的和親對象是個殘疾皇子茅姜,可洞房花燭夜當晚...
    茶點故事閱讀 43,435評論 2 348

推薦閱讀更多精彩內(nèi)容