釋放堆內(nèi)存汪茧,Rust是怎么做的椅亚?所有權(quán)!

本篇談下Rust語言的核心概念:所有權(quán)舱污。

這個(gè)概念是支撐Rust在編譯期做內(nèi)存安全檢查的核心機(jī)制呀舔,也正是因?yàn)檫@個(gè)特性,我們認(rèn)為Rust是內(nèi)存安全底層語言扩灯。雖然帶GC垃圾回收器的語言雖然也是內(nèi)存安全的媚赖,但由于GC的存在,已與底層無緣驴剔。

ownership

棧和堆

當(dāng)說到語言的內(nèi)存管理時(shí)省古,通常指的是對(duì)于堆的管理,而棧的使用都是自動(dòng)的丧失,通常都不需要程序員特別關(guān)心豺妓。

棧,是一種數(shù)據(jù)“后進(jìn)先出”的存取方式,速度非沉帐茫快训堆。《Rust權(quán)威指南》里對(duì)于棧的形容非常貼切:

你可以把棧上的操作想象成堆放盤子:當(dāng)你需要放置盤子時(shí)白嘁,你只能將它們放置在棧的頂部坑鱼,而當(dāng)你需要取出盤子時(shí),你也只能從頂部取出絮缅。

源代碼的層面鲁沥,一般已知大小、聲明性的變量都會(huì)使用棧上的空間耕魄。當(dāng)然画恰,系統(tǒng)默認(rèn)給到程序的棧空間都不大吸奴,Windows的默認(rèn)棧只有1M允扇。

windows stack

堆,可以理解為在編譯期無法確定的內(nèi)存開銷则奥,需要在運(yùn)行期動(dòng)態(tài)申請(qǐng)考润。堆沒有棧高效,通常要向系統(tǒng)申請(qǐng)读处,系統(tǒng)經(jīng)過搜索糊治,找到一塊足夠大的空間,才能鎖定交付档泽。堆所能申請(qǐng)到的空間俊戳,相比棧大很多揖赴,通常是系統(tǒng)的虛擬內(nèi)存大小的級(jí)別馆匿,比如32位系統(tǒng)有4G的虛擬內(nèi)存空間,那么可以申請(qǐng)到2G~3G大小的內(nèi)存空間燥滑。

而能否直接進(jìn)行堆內(nèi)存的操作渐北,可以粗略的將編程語言分為兩類,底層和高級(jí):

  • 能夠直接手工操作的語言铭拧,這類語言有著最大的靈活性赃蛛、執(zhí)行效率高,多用于系統(tǒng)編程搀菩,代表語言:C呕臂,C++。

  • 不能直接操作的語言肪跋,這類語言通常帶有GC垃圾自動(dòng)回收器歧蒋,性能會(huì)受一定影響,但是開發(fā)效率高,多用于業(yè)務(wù)編程谜洽,代表語言:Java萝映,Python。

本篇要介紹的Rust的所有權(quán)機(jī)制阐虚,屬于前者序臂,具備了底層語言的靈活性;但同時(shí)卻能避免手工操作堆內(nèi)存帶來的危險(xiǎn)性实束,具備了高級(jí)語言的高效性和安全性奥秆,可謂魚和熊掌可以兼得

接下來咸灿,為了解釋清楚Rust的堆操作機(jī)制吭练,我們先逐一看看各種堆操作方式。

堆管理方法一:純手工

80后程序員一般都經(jīng)歷過C++98的洗禮析显,那段時(shí)光鲫咽,關(guān)鍵字new和delete的魔咒就未消停過

下面代碼是創(chuàng)建一個(gè)100x1的灰度圖像所需要的空間谷异,但是對(duì)于一個(gè)“老練的”程序員分尸,總要把下面情況爛熟于心:

  • 總需要在圖像使用后,記得銷毀它歹嘹,否則直接造成內(nèi)存泄露箩绍;

  • 銷毀后,原來的指針會(huì)變成“野指針”尺上,如果再次使用材蛛,或者重復(fù)釋放被重新分配的內(nèi)存,都會(huì)導(dǎo)致無法預(yù)測(cè)的錯(cuò)誤怎抛,于是我們干脆把指針變量設(shè)為NULL卑吭;

  • 可能有段邏輯還會(huì)嘗試使用它,最好先判斷下它不為NULL马绝,避免拋異常豆赏;

上面的實(shí)踐,如果一個(gè)不小心富稻,bug就潛伏進(jìn)來掷邦,而且工程越大越難找。程序員的心智壓力可見一斑椭赋,這也是為什么我們需要《Effective C++》這本書的原因抚岗。

 {
    unsigned __int8* p_gray = new unsigned __int8[100]();

    delete p_gray;
    p_gray = NULL;

    if (p_gray != NULL)
    {
        cout << p_gray[0] << endl;
    }
}

堆管理方法二:GC垃圾回收

同樣一句創(chuàng)建8位字節(jié)數(shù)組的Java代碼,程序員只要“拿來”即可哪怔,“還回去”的事則不必親自費(fèi)心宣蔚。那誰操心呢廷痘?總得有人操心——JVM,Java虛擬機(jī)件已,更準(zhǔn)確的說笋额,是其中的自動(dòng)垃圾回收器。

自動(dòng)垃圾回收篷扩,因?yàn)椴皇浅绦騿T直接的意圖指令兄猩,所以GC就得靠自己分析垃圾特征,見機(jī)行事鉴未。自動(dòng)化內(nèi)存管理是實(shí)現(xiàn)了枢冤,但天下沒有免費(fèi)的午餐,回收時(shí)所需要的一小段時(shí)間铜秆,會(huì)讓整個(gè)Java程序進(jìn)入臭名昭著的“Stop-the-World”狀態(tài)淹真。

{
    byte[] arrayRefVar = new byte[100];
}

堆管理方法三:所有權(quán)

Rust作為靜態(tài)編譯型語言,顯然沒有運(yùn)行期虛擬機(jī)的夾持连茧,那么想要做到內(nèi)存安全核蘸,就得從兩個(gè)方面下手:

  • 自動(dòng)化內(nèi)存管理;

  • 把內(nèi)存安全檢查提前到編譯期啸驯;

做到第一點(diǎn)并不難客扎,其實(shí)RAII(Resource Acquisition Is Initialization)已經(jīng)在C++有著很廣泛的應(yīng)用了。

RAII的思想是:資源的有效期與持有資源的對(duì)象的生命期嚴(yán)格綁定罚斗,即由對(duì)象的構(gòu)造函數(shù)完成資源的分配徙鱼,同時(shí)由析構(gòu)函數(shù)完成資源的釋放。在這種要求下针姿,只要對(duì)象能正確的析構(gòu)袱吆,就不會(huì)出現(xiàn)資源泄露問題。

C++應(yīng)用RAII是以模式(Pattern)距淫,或者最佳實(shí)踐這種松散方式來實(shí)現(xiàn)的绞绒。Rust要想做到第二點(diǎn),就需要把這種思想集成進(jìn)語言本身溉愁,讓編譯器能看得懂处铛。

Rust提出了所有權(quán):

  • Rust中的每個(gè)值都有一個(gè)對(duì)應(yīng)的變量作為它的所有者饲趋;

  • 在同一時(shí)間內(nèi)拐揭,只有且僅有一個(gè)所有者;

  • 當(dāng)所有者離開自己的作用域時(shí)奕塑,它持有的值就會(huì)被釋放掉堂污。

我們用Rust再實(shí)現(xiàn)一次創(chuàng)建8位無符號(hào)整數(shù)數(shù)組:

{
 let v: Vec<u8> = vec![0;100];
} // v作為數(shù)組的所有者,在離開作用域時(shí)龄砰,銷毀了所持有的內(nèi)存盟猖。</pre>

和Java一樣讨衣,只需要一行代碼就完成了在堆上的內(nèi)存申請(qǐng)。但Rust做得更多——在離開作用域的同時(shí)式镐,確定性的銷毀了堆上的內(nèi)存反镇,而完全不需要一個(gè)拖泥帶水的GC。

可謂干凈利落娘汞,身手不凡歹茶。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
禁止轉(zhuǎn)載,如需轉(zhuǎn)載請(qǐng)通過簡(jiǎn)信或評(píng)論聯(lián)系作者你弦。
  • 序言:七十年代末惊豺,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子禽作,更是在濱河造成了極大的恐慌尸昧,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,402評(píng)論 6 499
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件旷偿,死亡現(xiàn)場(chǎng)離奇詭異烹俗,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)萍程,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,377評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門衷蜓,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人尘喝,你說我怎么就攤上這事磁浇。” “怎么了朽褪?”我有些...
    開封第一講書人閱讀 162,483評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵置吓,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我缔赠,道長(zhǎng)衍锚,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,165評(píng)論 1 292
  • 正文 為了忘掉前任嗤堰,我火速辦了婚禮戴质,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘踢匣。我一直安慰自己告匠,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,176評(píng)論 6 388
  • 文/花漫 我一把揭開白布离唬。 她就那樣靜靜地躺著后专,像睡著了一般。 火紅的嫁衣襯著肌膚如雪输莺。 梳的紋絲不亂的頭發(fā)上戚哎,一...
    開封第一講書人閱讀 51,146評(píng)論 1 297
  • 那天裸诽,我揣著相機(jī)與錄音,去河邊找鬼型凳。 笑死丈冬,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的甘畅。 我是一名探鬼主播殷蛇,決...
    沈念sama閱讀 40,032評(píng)論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼橄浓!你這毒婦竟也來了粒梦?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,896評(píng)論 0 274
  • 序言:老撾萬榮一對(duì)情侶失蹤荸实,失蹤者是張志新(化名)和其女友劉穎匀们,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體准给,經(jīng)...
    沈念sama閱讀 45,311評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡泄朴,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,536評(píng)論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了露氮。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片祖灰。...
    茶點(diǎn)故事閱讀 39,696評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖畔规,靈堂內(nèi)的尸體忽然破棺而出局扶,到底是詐尸還是另有隱情,我是刑警寧澤叁扫,帶...
    沈念sama閱讀 35,413評(píng)論 5 343
  • 正文 年R本政府宣布三妈,位于F島的核電站,受9級(jí)特大地震影響莫绣,放射性物質(zhì)發(fā)生泄漏畴蒲。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,008評(píng)論 3 325
  • 文/蒙蒙 一对室、第九天 我趴在偏房一處隱蔽的房頂上張望模燥。 院中可真熱鬧,春花似錦掩宜、人聲如沸蔫骂。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽纠吴。三九已至,卻和暖如春慧瘤,著一層夾襖步出監(jiān)牢的瞬間戴已,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,815評(píng)論 1 269
  • 我被黑心中介騙來泰國打工锅减, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留糖儡,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,698評(píng)論 2 368
  • 正文 我出身青樓怔匣,卻偏偏與公主長(zhǎng)得像握联,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子每瞒,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,592評(píng)論 2 353