iOS底層原理 - 內(nèi)存對齊&& malloc理解

1.回顧之前

? 前面我們講過alloc的一些底層探索中缠导,在分配內(nèi)存的時候有涉及到內(nèi)存對齊的概念沦泌。instanceSize()中alignedInstanceSize()內(nèi)存分配粗略的講到了內(nèi)存對齊的概念瞄勾,下面我們來詳細了解一下

2.內(nèi)存對齊-初探

? 什么是內(nèi)存對齊?是否有很多問號弥激,剛接觸到這個概念的時候进陡,也是很疑惑。概念:編譯器在讀取內(nèi)存地址的時候秆撮,會按照一定的偏移量去讀人谋簟;比如在一個寫的struct里面定義一定變量职辨,里面變量大小都是4字節(jié)盗蟆,8字節(jié),16 字節(jié)的舒裤,而且sizeof()大小也不是里面定義大小一樣(大于里面定義的字節(jié)大性省)。

? 為什么呢腾供?下面??我們來說明一下原因

內(nèi)存對齊的原因

? 1.讀取效率:cpu數(shù)據(jù)訪問效率仆邓。一般平臺系統(tǒng)會從基數(shù)位地址開始讀取,那么需要兩個周期才能拼湊成32bit/64bit伴鳖。所以從效率上考慮系統(tǒng)會吧基數(shù)字節(jié)自動補齊成偶數(shù)位一個周期便可以讀取完成节值,提高了讀取效率

? 2.硬件原因:一部分平臺不能訪問任意地址上的數(shù)據(jù)的,需要訪問特定指定類型的數(shù)據(jù)榜聂,不然會出現(xiàn)異常(具體哪些平臺尚不了解搞疗,需從網(wǎng)上了解...)

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

1)數(shù)據(jù)成員對其規(guī)則:結(jié)構(gòu)體(struct)或聯(lián)合體(union)的數(shù)據(jù)成員,第一個數(shù)據(jù)成員放在offset為0的地方须肆,以后每個數(shù)據(jù)成員存儲的起始位置要從該成員大小或者成員的子成員大心淠恕(只要該成員有子成員,比如數(shù)組豌汇、結(jié)構(gòu)體等)的整數(shù)倍開始幢炸。

? 總結(jié):int為4字節(jié),則要從4的整數(shù)倍開始存儲

2)結(jié)構(gòu)體作為成員:如果一個結(jié)構(gòu)體內(nèi)部包含其他結(jié)構(gòu)體成員拒贱,則結(jié)構(gòu)體成員要從其內(nèi)部最大元素大小的整數(shù)倍地址開始存儲宛徊。

總結(jié):struct x中包含char ,int柜思,double岩调,float等元素,那應該從8(double字節(jié)大猩呐獭)的整數(shù)倍開始存儲

3)收尾工作:結(jié)構(gòu)體的總大小,也就是sizeof的結(jié)果缰揪,必須是其內(nèi)部最大成員的整數(shù)倍陨享,不足的需要補齊葱淳。在malloc中總是為16的倍數(shù)。

內(nèi)存對齊-實戰(zhàn)

舉個栗子1

struct StructOne {
 char a;         // 1字節(jié)
 double b;       // 8字節(jié)
 int c;          // 4字節(jié)
 short d;        // 2字節(jié)
} Struct1;
?
struct StructTwo {
 double b;       // 8字節(jié)
 int c;          // 4字節(jié)
 char a;         // 1字節(jié)
 short d;        // 2字節(jié)
} Struct2;
?
struct StructOThree {
 double b;       // 8字節(jié)
 char a;         // 1字節(jié)
 int c;          // 4字節(jié)
 short d;        // 2字節(jié)
} Struct3;
?
NSLog(@"%lu---%lu---%lu",sizeof(Struct1),sizeof(Struct2),sizeof(Struct3));

輸出結(jié)果為: 24—16—24

我們從內(nèi)存對齊原則看抛姑,上面三個結(jié)構(gòu)體在內(nèi)存棧中的分布應該是這樣的:


截屏2020-05-11 下午4.05.57.png

搜狐公眾號推送的一片文章-內(nèi)存布局(推薦閱讀赞厕,很詳細)

類對象內(nèi)存開辟

1.準備工作

LLDB調(diào)試知識:

①: x/4gx 對象表示輸出4個16進制的8字節(jié)地址空間(x表示16進制,4表示4個定硝,g表示8字節(jié)為單位皿桑,等同于x/4xg 對象

②:pop:p表示"expression"——打印對象指針;而po是"expression -O"——打印對象本身

③:Xcode查看內(nèi)存地址 debug->Debug Workflow->view memory

? 舉個栗子??

@interface WXPerson : NSObject
@property (nonatomic, assign) NSInteger age;
@property (nonatomic, copy) NSString *name;
@property (nonatomic, assign) long height;
@property (nonatomic, assign) char c1;
@property (nonatomic, assign) float m_float;
@end
?
#import "WXPerson.h"
#import <objc/runtime.h>
#import <malloc/malloc.h>
?
int main(int argc, const char * argv[]) {
 @autoreleasepool {
 // insert code here...
 WXPerson *p = [[WXPerson alloc] init];
 p.name = @"Kaemi";  //  NSString  8
 p.age = 18;         //  int       4
 p.height = 188;     //  long      8
 p.c1 =  'a';        //  char      1
 p.m_float =  12.6;   //  float     4
 NSLog(@"sizeof:%lu---申請內(nèi)存大小為:%lu——-系統(tǒng)開辟內(nèi)存大小為:%lu",sizeof([p class]),class_getInstanceSize([p class]),malloc_size((__bridge const void *)(p)));
 }
 return 0;
}

輸出結(jié)果為:sizeof:8---申請內(nèi)存大小為:40——-系統(tǒng)開辟內(nèi)存大小為:48

幾個知識點:sizeof() class_getInstanceSize() malloc_size() 是什么

sizeof:它是一個運算符蔬啡,在編譯時就可以獲取類型所占內(nèi)存的大小

class_getInstanceSize:依賴于<objc/runtime.h>诲侮,返回創(chuàng)建一個實例對象所需內(nèi)存大小,不考慮malloc函數(shù)的話箱蟆,內(nèi)存對齊一般是以 8 字節(jié)對齊

malloc_size:依賴于<malloc/malloc.h>沟绪,返回系統(tǒng)實際分配的內(nèi)存大小,在Mac空猜、iOS中的malloc函數(shù)分配的內(nèi)存大小總是 16 的倍數(shù)

那為啥class_getInstanceSize返回的值是40绽慈,malloc_size返回時48呢?下面??我們來分析一下

3.malloc源碼分析

內(nèi)存開辟辈毯,我們在alloc 內(nèi)存開辟中還遺留了一個問題:obj = (id)calloc(1, size) 坝疼,之前我們用objc源碼無法斷點下手,現(xiàn)在我們可以用libmalloc源碼來分析一下

cooci malloc分析(很詳細)

libmalloc源碼中新建target谆沃,按照objc源碼中的方式調(diào)用

void *p = calloc(1, 40);

斷點malloc_zone_calloc();


malloc_1.png

通過調(diào)試臺看一下具體實現(xiàn)

(lldb) p zone->calloc
(void *(*)(_malloc_zone_t *, size_t, size_t)) $0 = 0x000000010031cd14 (.dylib`default_zone_calloc at malloc.c:249)

確定default_zone_calloc钝凶,再搜索它的實現(xiàn)源碼

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);
}

繼續(xù)用lldb查看:

(lldb) p zone->calloc
(void *(*)(_malloc_zone_t *, size_t, size_t)) $0 = 0x000000010031e33f (.dylib`nano_calloc at nano_malloc.c:878)
malloc_2.png

再進去


malloc_3.png

最后我們會到這邊

/*
 * #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; 
 }
 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;
}

class_getInstanceSize 是對象對8字節(jié)對齊,根據(jù)內(nèi)存對齊原則可知分配了40大泄鼙小腿椎;

malloc_size中的48是怎么來的。這里有多個size_t類夭咬,斷點調(diào)試看了下的size是我們傳進來的40啃炸,而slot_bytes剛好是我們的目標48,

在經(jīng)過 (40 + 16 - 1) >> 4 << 4 操作后卓舵,結(jié)果為48南用,也就是16的整數(shù)倍——即16字節(jié)對齊

malloc_size 系統(tǒng)對對象做了16字節(jié)對齊

總結(jié)

對象的屬性是8字節(jié)對齊

對象是16字節(jié)對齊

  • 因為內(nèi)存是連續(xù)的,通過 16 字節(jié)對齊規(guī)避風險和容錯掏湾,防止訪問溢出

  • 同時裹虫,也提高了尋址訪問效率,也就是空間換時間

最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
禁止轉(zhuǎn)載融击,如需轉(zhuǎn)載請通過簡信或評論聯(lián)系作者筑公。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市尊浪,隨后出現(xiàn)的幾起案子匣屡,更是在濱河造成了極大的恐慌封救,老刑警劉巖,帶你破解...
    沈念sama閱讀 212,383評論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件捣作,死亡現(xiàn)場離奇詭異誉结,居然都是意外死亡,警方通過查閱死者的電腦和手機券躁,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,522評論 3 385
  • 文/潘曉璐 我一進店門惩坑,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人也拜,你說我怎么就攤上這事以舒。” “怎么了搪泳?”我有些...
    開封第一講書人閱讀 157,852評論 0 348
  • 文/不壞的土叔 我叫張陵稀轨,是天一觀的道長。 經(jīng)常有香客問我岸军,道長奋刽,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,621評論 1 284
  • 正文 為了忘掉前任艰赞,我火速辦了婚禮佣谐,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘方妖。我一直安慰自己狭魂,他們只是感情好,可當我...
    茶點故事閱讀 65,741評論 6 386
  • 文/花漫 我一把揭開白布党觅。 她就那樣靜靜地躺著雌澄,像睡著了一般。 火紅的嫁衣襯著肌膚如雪杯瞻。 梳的紋絲不亂的頭發(fā)上镐牺,一...
    開封第一講書人閱讀 49,929評論 1 290
  • 那天,我揣著相機與錄音魁莉,去河邊找鬼睬涧。 笑死,一個胖子當著我的面吹牛旗唁,可吹牛的內(nèi)容都是我干的畦浓。 我是一名探鬼主播,決...
    沈念sama閱讀 39,076評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼检疫,長吁一口氣:“原來是場噩夢啊……” “哼讶请!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起屎媳,我...
    開封第一講書人閱讀 37,803評論 0 268
  • 序言:老撾萬榮一對情侶失蹤秽梅,失蹤者是張志新(化名)和其女友劉穎抹蚀,沒想到半個月后剿牺,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體企垦,經(jīng)...
    沈念sama閱讀 44,265評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,582評論 2 327
  • 正文 我和宋清朗相戀三年晒来,在試婚紗的時候發(fā)現(xiàn)自己被綠了钞诡。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,716評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡湃崩,死狀恐怖荧降,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情攒读,我是刑警寧澤朵诫,帶...
    沈念sama閱讀 34,395評論 4 333
  • 正文 年R本政府宣布,位于F島的核電站薄扁,受9級特大地震影響剪返,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜邓梅,卻給世界環(huán)境...
    茶點故事閱讀 40,039評論 3 316
  • 文/蒙蒙 一脱盲、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧日缨,春花似錦钱反、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,798評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至毅待,卻和暖如春尚卫,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背恩静。 一陣腳步聲響...
    開封第一講書人閱讀 32,027評論 1 266
  • 我被黑心中介騙來泰國打工焕毫, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人驶乾。 一個月前我還...
    沈念sama閱讀 46,488評論 2 361
  • 正文 我出身青樓显沈,卻偏偏與公主長得像,于是被迫代替她去往敵國和親劈狐。 傳聞我的和親對象是個殘疾皇子尤蛮,可洞房花燭夜當晚...
    茶點故事閱讀 43,612評論 2 350