女人夜夜春高潮爽A∨片传媒_国产精品VIDEOS麻豆_在线精品亚洲一区二区三区_亚洲熟妇无码av

編程代碼
新聞詳情

為什么要理解內存管理?

發布時間:2020-12-23 09:17:34 瀏覽次(ci)數:2585

內(nei)存,以及(ji)編(bian)程(cheng)語言(yan)如何管理內(nei)存,是(shi)一個(ge)讓開發者們(men)頭疼不已的問題(ti)。我(wo)們(men)所(suo)寫的程(cheng)序時刻不停地分配著(zhu)內(nei)存,但(dan)我(wo)們(men)卻很難搞(gao)清楚,這一切到底是(shi)怎么發生的。


什么是內存

存(cun)儲(chu)(chu)空(kong)間(jian),正如它一(yi)開始所定義的(de),是我們(men)存(cun)儲(chu)(chu)特定信息,以備(bei)之(zhi)后使(shi)用的(de)地方,這(zhe)種存(cun)儲(chu)(chu)可能是永久的(de)(直(zhi)到(dao)我們(men)手動刪除(chu)),也(ye)可能是臨時(shi)的(de)(直(zhi)到(dao)電腦自動刪除(chu))。實(shi)際(ji)上,我們(men)和電腦之(zhi)間(jian)的(de)每一(yi)次交互,都涉及信息的(de)存(cun)儲(chu)(chu)。比如說(shuo),打開一(yi)個瀏覽器時(shi),它的(de)執行步(bu)驟就從永久存(cun)儲(chu)(chu)(硬(ying)盤(pan))加載到(dao)臨時(shi)存(cun)儲(chu)(chu)(內存(cun)RAM)中。

主存儲,或者說 RAM,是電腦使用的內部存儲空間,有別于 USB 、硬盤之類的外部存儲設備。電腦可以與內存直接交互,所有程序也必須加載到內存中才能執行。有時,整個程序都會被加載到內存中,也有時,只有程序的一部分(一個進程)被加載到內存中——這個機制被叫做動態加(jia)載。如果這部分程序依賴于另一個程序,那么,還會有一個動態鏈接機制建(jian)立起這個(ge)程序與(yu)主程序之間(jian)的關(guan)系(xi)。

內(nei)存管理影響到電腦中的每一(yi)個程序(xu),極為關鍵,因此,現代操(cao)作(zuo)系統都有一(yi)套復雜的機制(zhi)來完成這項工(gong)作(zuo)。通過各(ge)個層(ceng)次(硬(ying)件層(ceng)、操(cao)作(zuo)系統層(ceng)、應用(yong)軟件層(ceng))的協調與控制(zhi),確(que)保內(nei)存使用(yong)合理高效。

本文聚焦于操作系統與(yu)應用軟件(jian)中內存(cun)(cun)管理。在系統層,內存(cun)(cun)管理主要涉及(ji)特定存(cun)(cun)儲塊(可以被理解(jie)為地址與(yu)空(kong)間(jian))的(de)(de)分配;在應用層,內存(cun)(cun)管理主要涉及(ji)向系統發送內存(cun)(cun)空(kong)間(jian)請(qing)求(qiu),以及(ji)確保程序定義的(de)(de)對象與(yu)數(shu)據結構(gou)有足夠的(de)(de)存(cun)(cun)儲空(kong)間(jian)(內存(cun)(cun)的(de)(de)分配、重(zhong)新分配以及(ji)釋放)。

當一(yi)個(ge)程(cheng)序(xu)申請(qing)一(yi)段內存時,一(yi)個(ge)“分配(pei)者”會(hui)負責將內存分配(pei)給它,并在不再需要(yao)的時候釋放出來,以(yi)(yi)(yi)供(gong)重新分配(pei)。這個(ge)過程(cheng)可以(yi)(yi)(yi)手動控制,也可以(yi)(yi)(yi)自動完成(cheng),主要(yao)取決于編程(cheng)語言的特性以(yi)(yi)(yi)及程(cheng)序(xu)員(yuan)自己(ji)的選(xuan)擇。

手(shou)動(dong)(dong)(dong)內(nei)(nei)存管(guan)理可以理解為程序員通過自(zi)(zi)己的(de)代碼分(fen)配或釋(shi)放內(nei)(nei)存。比(bi)較著名的(de),是 C 語(yu)言(yan)使用的(de)動(dong)(dong)(dong)態內(nei)(nei)存分(fen)配技術(shu)。不過,得(de)力(li)于 ObjectiveC 和 Swift 的(de)大力(li)推廣(guang),現在流(liu)行的(de)大多數編程語(yu)言(yan)都(dou)通過垃圾回收器或自(zi)(zi)動(dong)(dong)(dong)引用計數(ARC)實現了自(zi)(zi)動(dong)(dong)(dong)內(nei)(nei)存管(guan)理。


內存管理的陷阱

錯誤的(de)內存操作會破壞內存區(qu)塊的(de)分配與釋放過(guo)程(cheng)(cheng),導(dao)致很(hen)嚴(yan)(yan)重(zhong)的(de)后果。從(cong)更(geng)高層面看,內存區(qu)塊總是會恢復(fu)正常的(de),一(yi)個(ge)簡單的(de)錯誤似(si)乎并沒有那么嚴(yan)(yan)重(zhong),但系(xi)統中總是同時運行著成百(bai)上千個(ge)進程(cheng)(cheng),不(bu)可能(neng)都卡(ka)在(zai)那里,等著某個(ge)內存區(qu)塊恢復(fu)正常。

于是,這些錯誤會(hui)(hui)用(yong)光程序運(yun)行所(suo)需的必要內存(cun)空間,或(huo)者(zhe)更糟糕的是,如(ru)果區塊被(bei)(bei)錯誤地(di)釋(shi)放或(huo)分配(pei),區塊中存(cun)儲的敏感信息(xi),比如(ru)密(mi)碼、密(mi)鑰或(huo)者(zhe)其它隱私信息(xi),會(hui)(hui)被(bei)(bei)攻擊者(zhe)所(suo)竊取。

以下是錯誤的(de)內(nei)存操(cao)作產生的(de)常(chang)見(jian)后(hou)果:

算術或整數溢出(Arithmetic or integer overflows)

由于錯誤的算術計算,原來分配的內存(cun)區塊無(wu)法(fa)存(cun)儲(chu)最后(hou)的結果。比如說,一(yi)個(ge)程(cheng)序可能(neng)定義了(le)一(yi)個(ge)占用 8 位內存(cun)的值(zhi),只能(neng)存(cun)儲(chu) -128 到 +127 之間的數字,假設程(cheng)序先將這個(ge)數字賦值(zhi)為(wei) 127,之后(hou)又加了(le) 1,就會導致一(yi)個(ge)預期外的結果,因為(wei) 8 位內存(cun)空間無(wu)法(fa)存(cun)儲(chu) 128 這個(ge)值(zhi)。

這(zhe)個 Bug 由 Brumley, Chiueh 和 Johnson 在 2012 年定義(yi),具體(ti)描述是(shi),“一(yi)(yi)個變(bian)(bian)量(liang)的值(zhi)超出(chu)了機(ji)器存儲(chu)這(zhe)個值(zhi)所(suo)用字節(jie)的表示(shi)范圍(wei)”。產生這(zhe)個 Bug 的原(yuan)因很多,比(bi)如向(xiang)上(shang)溢(yi)出(chu)、向(xiang)下溢(yi)出(chu)、數(shu)據(ju)截取、符號錯誤等,主要是(shi)由于錯誤定義(yi)的語句或(huo)整數(shu)操作(zuo),而(er)程序員(yuan)(yuan)要定位問(wen)題(ti)(ti)往(wang)往(wang)很困難。不(bu)同語言處理這(zhe)個問(wen)題(ti)(ti)的方式也不(bu)一(yi)(yi)樣——例如,Smalltalk 與 Scheme 會自動(dong)升級(ji)變(bian)(bian)量(liang)類型,而(er)其(qi)它一(yi)(yi)些語言則把問(wen)題(ti)(ti)留給程序員(yuan)(yuan)自己。

內存泄露(Memory Leak)

如(ru)(ru)果一個程序一直向系(xi)(xi)統(tong)申請,但不釋放(fang)內(nei)(nei)存(cun)(cun)(cun)——也就是說,告訴系(xi)(xi)統(tong)哪些內(nei)(nei)存(cun)(cun)(cun)可以重新利用了——就會導(dao)致(zhi)內(nei)(nei)存(cun)(cun)(cun)泄露,程序最終(zhong)會用完所(suo)有可用內(nei)(nei)存(cun)(cun)(cun)。另外,如(ru)(ru)果程序中的某個對象被存(cun)(cun)(cun)儲在內(nei)(nei)存(cun)(cun)(cun)中,但運行(xing)中的代碼實際上已經沒法訪問(wen)到(dao)它了,也會導(dao)致(zhi)同樣(yang)問(wen)題(ti)。

段錯誤(Segmentation faults)

當某個(ge)程序訪問它(ta)沒有(you)權(quan)限(xian)訪問的(de)、另作(zuo)它(ta)用的(de)內存空(kong)間(jian),或(huo)(huo)者對某部分內存執行超(chao)越權(quan)限(xian)的(de)操(cao)作(zuo),比如試圖對只(zhi)讀內容進行寫操(cao)作(zuo)時,就會導致段(duan)錯誤。段(duan)錯誤可能導致程序掛起、崩潰(kui)或(huo)(huo)退出。

緩沖區溢出(Buffer overflows)

當程序要寫入(ru)的(de)內容超過(guo)了被分配的(de)空間長度,它(ta)繼續寫入(ru)到之(zhi)后(hou)的(de),另作它(ta)用,或者(zhe)沒有寫權(quan)限(xian)的(de)內存空間時,就會導致緩沖區(qu)溢(yi)出(chu)(chu)。緩沖區(qu)溢(yi)出(chu)(chu)也會使程序掛起、崩潰或退出(chu)(chu)。

刪除錯誤(Double delete)

當(dang)程(cheng)序試圖(tu)刪除(chu)一(yi)個(ge)已經被刪除(chu)的(de)對象,因而(er)導致堆污染或(huo)者段(duan)錯誤(wu)時,就叫刪除(chu)錯誤(wu)。刪除(chu)錯誤(wu)也(ye)可以(yi)認為(wei)是段(duan)錯誤(wu)的(de)一(yi)個(ge)子集。


手動 VS 自動內存管理

對程序(xu)員來說(shuo),最常見的內存(cun)(cun)(cun)問題就(jiu)是(shi)如(ru)何操作(zuo)內存(cun)(cun)(cun)的問題——如(ru)果說(shuo)系統可(ke)以把內存(cun)(cun)(cun)分配(pei)給程序(xu),那么,程序(xu)所(suo)使(shi)用(yong)的編程語言是(shi)手(shou)動還是(shi)自動完(wan)成內存(cun)(cun)(cun)分配(pei)的呢(ni)?以及更(geng)重要(yao)的,這種分配(pei)方式會導致什么結果呢(ni)?

手(shou)動內存管理(li)是指(zhi)(zhi)在特定語(yu)言中(zhong),程序員必須通(tong)過自己的(de)(de)代碼來管理(li)內存,與(yu)之(zhi)相對地,自動內存管理(li)是指(zhi)(zhi)程序員不需要或(huo)(huo)基本不需要執行什(shen)么動作來操作內存。我們這里所(suo)說的(de)(de)“操作”和(he)“管理(li)”,是指(zhi)(zhi)申請、重(zhong)新分配(pei)內存,或(huo)(huo)者釋放掉我們認為已(yi)經(jing)成為“垃圾(ji)”的(de)(de)內存空間。

直到上世紀 90 年代中期(qi),主流(liu)編程(cheng)語言都(dou)支(zhi)持手動內(nei)存管理,即使在今天也依然(ran)如此(以關鍵(jian)詞 “new” 或 “alloc” 的(de)(de)形式)。不過,這(zhe)僅(jin)僅(jin)是(shi)因(yin)為對(dui)(dui)(dui)象(xiang)(xiang)(xiang)創建,也就(jiu)是(shi)為對(dui)(dui)(dui)象(xiang)(xiang)(xiang)分(fen)配內(nei)存的(de)(de)過程(cheng)很(hen)容(rong)易而已——程(cheng)序(xu)員在創建對(dui)(dui)(dui)象(xiang)(xiang)(xiang)的(de)(de)時候(hou),可(ke)以清楚地知(zhi)(zhi)道(dao)對(dui)(dui)(dui)象(xiang)(xiang)(xiang)的(de)(de)大小、名稱以及初始化(hua)過程(cheng)。然(ran)而,銷毀(hui)(hui)對(dui)(dui)(dui)象(xiang)(xiang)(xiang)就(jiu)困難多了,由于銷毀(hui)(hui)過程(cheng)往往在對(dui)(dui)(dui)象(xiang)(xiang)(xiang)創建很(hen)久之后(hou)才觸(chu)發,程(cheng)序(xu)員可(ke)能并不知(zhi)(zhi)道(dao)對(dui)(dui)(dui)象(xiang)(xiang)(xiang)的(de)(de)大小。更(geng)麻煩的(de)(de)是(shi),程(cheng)序(xu)員可(ke)能也不知(zhi)(zhi)道(dao)具體(ti)在哪個(ge)時間點應該銷毀(hui)(hui)對(dui)(dui)(dui)象(xiang)(xiang)(xiang),很(hen)有可(ke)能,軟(ruan)件中的(de)(de)某(mou)部分(fen)代碼依然(ran)在使用這(zhe)個(ge)對(dui)(dui)(dui)象(xiang)(xiang)(xiang)。

如(ru)之前所說(shuo),如(ru)果不能正確地初始化或銷(xiao)毀對象,就(jiu)會(hui)導(dao)致內存(cun)錯誤。編程語言如(ru)何處(chu)理(li)內存(cun)錯誤取決于它的(de)具體實現:大多(duo)情況下,內存(cun)錯誤會(hui)導(dao)致“未定(ding)義行(xing)為(undefined behavior)”——也就(jiu)是說(shuo),說(shuo)不準會(hui)發生(sheng)什(shen)么。(注意,在準確的(de)手(shou)動內存(cun)管理(li)下,一切都是確定(ding)的(de),程序員總是清楚一個對象什(shen)么時(shi)候被創建或被銷(xiao)毀。)

1959 年,一個內存管理的新概念——垃圾回收(shou)——被引入 Lisp 編程語(yu)言。垃圾(ji)回收是自動內存(cun)管(guan)(guan)理(li)中(zhong)最著名(ming)的(de)一個例子,通(tong)過垃圾(ji)回收,之后不再使用(yong)的(de)對(dui)象會被銷毀,空間會被釋放。這種技術減少了 Bug,提高了內存(cun)管(guan)(guan)理(li)水平。垃圾(ji)回收的(de)具體實現采用(yong)了多種策略,包(bao)括(kuo)對(dui)象追蹤、引用(yong)計(ji)數、時(shi)間戳(chuo)、心跳等。

其它自(zi)動內(nei)存(cun)管理技術包(bao)括基于棧的內(nei)存(cun)管理(stack-based memory allocation)、基于作用域的內(nei)存(cun)管理(region-based memory management)、自(zi)動引用計(ji)數(ARC)等。不過,這(zhe)些技術都(dou)存(cun)在(zai)一(yi)些性(xing)能問題,也帶來了某種(zhong)不確定性(xing),因為程序(xu)員并(bing)不能準確地(di)知道對象(xiang)是在(zai)什么時候被銷毀(hui)的。

當然(ran),手動(dong)內(nei)存管(guan)理(li)與自動(dong)內(nei)存管(guan)理(li)都(dou)還被今天的編(bian)程語言廣(guang)泛應用:前(qian)者以(yi)(yi) C 語言家族為代(dai)表(biao),后者以(yi)(yi) Lisp、Java 以(yi)(yi)及其(qi)它(ta)眾多語言為代(dai)表(biao)。事實(shi)上,大多數(shu)語言都(dou)混(hun)合使用這兩種技術(shu):如前(qian)文所說(shuo),通過手動(dong)方式分配內(nei)存,通過垃圾(ji)回收技術(shu)釋放內(nei)存。


結論

如我們所見,電腦幫(bang)助(zhu)人(ren)類解決復(fu)雜問題的方式,讓程(cheng)序員有一種(zhong)(zhong)“宇(yu)宙之(zhi)主”的感覺。我們也注意到,這個宇(yu)宙存在(zai)著(zhu)種(zhong)(zhong)種(zhong)(zhong)規則和(he)限制,其中一個,就是(shi)內存總是(shi)有限的。不過,正如哈(ha)姆雷特所說,作為程(cheng)序員,我們依然可以“藏身果殼之(zhi)中,而(er)把自己看作擁有無限疆(jiang)域的君王。”

在線客(ke)服(fu)
客(ke)服電話
  • 0755-23712116
  • 13310869691