對(duì)普通進(jìn)程來(lái)說(shuō),能看到的其實(shí)是內(nèi)核提供的虛擬內(nèi)存疙驾,這些虛擬內(nèi)存還需要通過(guò)頁(yè)表凶伙,由系統(tǒng)映射為物理內(nèi)存。
當(dāng)進(jìn)程通過(guò) malloc() 申請(qǐng)?zhí)摂M內(nèi)存后它碎,系統(tǒng)并不會(huì)立即為其分配物理內(nèi)存函荣,而是在首次訪(fǎng)問(wèn)時(shí),才通過(guò)缺頁(yè)異常陷入內(nèi)核中分配內(nèi)存扳肛。
為了協(xié)調(diào) CPU 與磁盤(pán)間的性能差異傻挂,Linux 還會(huì)使用 Cache 和 Buffer ,分別把文件和磁盤(pán)讀寫(xiě)的數(shù)據(jù)緩存到內(nèi)存中挖息。
對(duì)應(yīng)用程序來(lái)說(shuō)金拒,動(dòng)態(tài)內(nèi)存的分配和回收,是既核心又復(fù)雜的一個(gè)邏輯功能模塊套腹。管理內(nèi)存的過(guò)程中绪抛,也很容易發(fā)生各種各樣的“事故”,比如电禀,
沒(méi)正確回收分配后的內(nèi)存幢码,導(dǎo)致了泄漏。
訪(fǎng)問(wèn)的是已分配內(nèi)存邊界外的地址尖飞,導(dǎo)致程序異常退出症副,等等。
用戶(hù)空間內(nèi)存包括多個(gè)不同的內(nèi)存段政基,比如只讀段贞铣、數(shù)據(jù)段、堆沮明、棧以及文件映射段等咕娄。這些內(nèi)存段正是應(yīng)用程序使用內(nèi)存的基本方式。
舉個(gè)例子珊擂,你在程序中定義了一個(gè)局部變量圣勒,比如一個(gè)整數(shù)數(shù)組 int data[64] ,就定義了一個(gè)可以存儲(chǔ) 64 個(gè)整數(shù)的內(nèi)存段摧扇。由于這是一個(gè)局部變量圣贸,它會(huì)從內(nèi)存空間的棧中分配內(nèi)存。
棧內(nèi)存由系統(tǒng)自動(dòng)分配和管理扛稽。一旦程序運(yùn)行超出了這個(gè)局部變量的作用域吁峻,棧內(nèi)存就會(huì)被系統(tǒng)自動(dòng)回收,所以不會(huì)產(chǎn)生內(nèi)存泄漏的問(wèn)題在张。
再比如用含,很多時(shí)候,我們事先并不知道數(shù)據(jù)大小帮匾,所以你就要用到標(biāo)準(zhǔn)庫(kù)函數(shù) malloc() _啄骇,_ 在程序中動(dòng)態(tài)分配內(nèi)存。這時(shí)候瘟斜,系統(tǒng)就會(huì)從內(nèi)存空間的堆中分配內(nèi)存缸夹。
堆內(nèi)存由應(yīng)用程序自己來(lái)分配和管理。除非程序退出螺句,這些堆內(nèi)存并不會(huì)被系統(tǒng)自動(dòng)釋放虽惭,而是需要應(yīng)用程序明確調(diào)用庫(kù)函數(shù) free() 來(lái)釋放它們。如果應(yīng)用程序沒(méi)有正確釋放堆內(nèi)存蛇尚,就會(huì)造成內(nèi)存泄漏芽唇。
內(nèi)存泄漏的危害非常大,這些忘記釋放的內(nèi)存取劫,不僅應(yīng)用程序自己不能訪(fǎng)問(wèn)匆笤,系統(tǒng)也不能把它們?cè)俅畏峙浣o其他應(yīng)用。內(nèi)存泄漏不斷累積勇凭,甚至?xí)谋M系統(tǒng)內(nèi)存疚膊。
雖然,系統(tǒng)最終可以通過(guò) OOM (Out of Memory)機(jī)制殺死進(jìn)程虾标,但進(jìn)程在 OOM 前寓盗,可能已經(jīng)引發(fā)了一連串的反應(yīng),導(dǎo)致嚴(yán)重的性能問(wèn)題璧函。
比如傀蚌,其他需要內(nèi)存的進(jìn)程,可能無(wú)法分配新的內(nèi)存蘸吓;內(nèi)存不足善炫,又會(huì)觸發(fā)系統(tǒng)的緩存回收以及 SWAP 機(jī)制,從而進(jìn)一步導(dǎo)致 I/O 的性能問(wèn)題等等库继。