常用的內(nèi)存管理方法有哪幾種
常用的內(nèi)存管理方法有哪幾種?下面是學(xué)習(xí)啦小編給大家收集整理的一些相關(guān)方法技巧,希望對(duì)大家有幫助!
常用的內(nèi)存管理方法
傳統(tǒng)的內(nèi)存整理軟件工作原理大概是:先申請(qǐng)一塊“巨大內(nèi)存”。因?yàn)槲锢韮?nèi)存幾乎全被內(nèi)存整理軟件占用,因此Windows被迫把其他軟件的內(nèi)存數(shù)據(jù)轉(zhuǎn)移到硬盤(pán)上的“虛擬內(nèi)存交換文件”(PageFile)中,完成這一過(guò)程之后內(nèi)存整理軟件就會(huì)釋放掉剛剛申請(qǐng)的內(nèi)存,至此整理過(guò)程完成,可用物理內(nèi)存顯著增加。
大體上都是那么回事,就是通過(guò)輔助空間,重新安排內(nèi)存內(nèi)容 ....
但是其中使用的算法,效率是有很大的區(qū)別的 ~~ <script type="text/javascript"><!-- google_ad_client = "pub-4403405132739389"; google_ad_width = 250; google_ad_height = 250; google_ad_format = "250x250_as"; google_ad_type = "text"; //2007-10-22: 250*250 google_ad_channel = "7687946060"; google_ui_features = "rc:10"; //--> </script><script type="text/javascript" src=pagead2.googlesyndication/pagead/show_ads.js"> </script>
拓荒時(shí)代
國(guó)內(nèi)的程序員大多是在 Java 語(yǔ)言中第一次感受到垃圾收集技術(shù)的巨大魅力的,許多人也因此把 Java 和垃圾收集看成了密不可分的整體。但事實(shí)上,垃圾收集技術(shù)早在 Java 語(yǔ)言問(wèn)世前 30 多年就已經(jīng)發(fā)展和成熟起來(lái)了, Java 語(yǔ)言所做的不過(guò)是把這項(xiàng)神奇的技術(shù)帶到了廣大程序員身邊而已。
如果一定要為垃圾收集技術(shù)找一個(gè)孿生兄弟,那么, Lisp 語(yǔ)言才是當(dāng)之無(wú)愧的人選。 1960 年前后誕生于 MIT 的 Lisp 語(yǔ)言是第一種高度依賴于動(dòng)態(tài)內(nèi)存分配技術(shù)的語(yǔ)言: Lisp 中幾乎所有數(shù)據(jù)都以“表”的形式出現(xiàn),而“表”所占用的空間則是在堆中動(dòng)態(tài)分配得到的。 Lisp 語(yǔ)言先天就具有的動(dòng)態(tài)內(nèi)存管理特性要求 Lisp 語(yǔ)言的設(shè)計(jì)者必須解決堆中每一個(gè)內(nèi)存塊的自動(dòng)釋放問(wèn)題(否則, Lisp 程序員就必然被程序中不計(jì)其數(shù)的 free 或 delete 語(yǔ)句淹沒(méi)),這直接導(dǎo)致了垃圾收集技術(shù)的誕生和發(fā)展——說(shuō)句題外話,上大學(xué)時(shí),一位老師曾告訴我們, Lisp 是對(duì)現(xiàn)代軟件開(kāi)發(fā)技術(shù)貢獻(xiàn)最大的語(yǔ)言。我當(dāng)時(shí)對(duì)這一說(shuō)法不以為然:布滿了圓括號(hào),看上去像迷宮一樣的 Lisp 語(yǔ)言怎么能比 C 語(yǔ)言或 Pascal 語(yǔ)言更偉大呢?不過(guò)現(xiàn)在,當(dāng)我知道垃圾收集技術(shù)、數(shù)據(jù)結(jié)構(gòu)技術(shù)、人工智能技術(shù)、并行處理技術(shù)、虛擬機(jī)技術(shù)、元數(shù)據(jù)技術(shù)以及程序員們耳熟能詳?shù)脑S多技術(shù)都起源于 Lisp 語(yǔ)言時(shí),我特別想向那位老師當(dāng)面道歉,并收回我當(dāng)時(shí)的幼稚想法。
知道了 Lisp 語(yǔ)言與垃圾收集的密切關(guān)系,我們就不難理解,為什么垃圾收集技術(shù)的兩位先驅(qū)者 J. McCarthy 和 M. L. Minsky 同時(shí)也是 Lisp 語(yǔ)言發(fā)展史上的重要人物了。 J. McCarthy 是 Lisp 之父,他在發(fā)明 Lisp 語(yǔ)言的同時(shí)也第一次完整地描述了垃圾收集的算法和實(shí)現(xiàn)方式; M. L. Minsky 則在發(fā)展 Lisp 語(yǔ)言的過(guò)程中成為了今天好幾種主流垃圾收集算法的奠基人——和當(dāng)時(shí)不少技術(shù)大師的經(jīng)歷相似, J. McCarthy 和 M. L. Minsky 在許多不同的技術(shù)領(lǐng)域里都取得了令人艷羨的成就。也許,在 1960 年代那個(gè)軟件開(kāi)發(fā)史上的拓荒時(shí)代里,思維敏捷、意志堅(jiān)定的研究者更容易成為無(wú)所不能的西部硬漢吧。
在了解垃圾收集算法的起源之前,有必要先回顧一下內(nèi)存分配的主要方式。我們知道,大多數(shù)主流的語(yǔ)言或運(yùn)行環(huán)境都支持三種最基本的內(nèi)存分配方式,它們分別是:
一、靜態(tài)分配( Static Allocation ):靜態(tài)變量和全局變量的分配形式。我們可以把靜態(tài)分配的內(nèi)存看成是家里的耐用家具。通常,它們無(wú)需釋放和回收,因?yàn)闆](méi)人會(huì)天天把大衣柜當(dāng)作垃圾扔到窗外。
二、自動(dòng)分配( Automatic Allocation ):在棧中為局部變量分配內(nèi)存的方法。棧中的內(nèi)存可以隨著代碼塊退出時(shí)的出棧操作被自動(dòng)釋放。這類(lèi)似于到家中串門(mén)的訪客,天色一晚就要各回各家,除了個(gè)別不識(shí)時(shí)務(wù)者以外,我們一般沒(méi)必要把客人捆在垃圾袋里掃地出門(mén)。
三、動(dòng)態(tài)分配( Dynamic Allocation ):在堆中動(dòng)態(tài)分配內(nèi)存空間以存儲(chǔ)數(shù)據(jù)的方式。堆中的內(nèi)存塊好像我們?nèi)粘J褂玫牟徒砑垼眠^(guò)了就得扔到垃圾箱里,否則屋內(nèi)就會(huì)滿地狼藉。像我這樣的懶人做夢(mèng)都想有一臺(tái)家用機(jī)器人跟在身邊打掃衛(wèi)生。在軟件開(kāi)發(fā)中,如果你懶得釋放內(nèi)存,那么你也需要一臺(tái)類(lèi)似的機(jī)器人——這其實(shí)就是一個(gè)由特定算法實(shí)現(xiàn)的垃圾收集器。
也就是說(shuō),下面提到的所有垃圾收集算法都是在程序運(yùn)行過(guò)程中收集并清理廢舊“餐巾紙”的算法,它們的操作對(duì)象既不是靜態(tài)變量,也不是局部變量,而是堆中所有已分配內(nèi)存塊。
引用計(jì)數(shù)( Reference Counting )算法
1960 年以前,人們?yōu)榕咛ブ械?Lisp 語(yǔ)言設(shè)計(jì)垃圾收集機(jī)制時(shí),第一個(gè)想到的算法是引用計(jì)數(shù)算法。拿餐巾紙的例子來(lái)說(shuō),這種算法的原理大致可以描述為:
午餐時(shí),為了把腦子里突然跳出來(lái)的設(shè)計(jì)靈感記下來(lái),我從餐巾紙袋中抽出一張餐巾紙,打算在上面畫(huà)出系統(tǒng)架構(gòu)的藍(lán)圖。按照“餐巾紙使用規(guī)約之引用計(jì)數(shù)版”的要求,畫(huà)圖之前,我必須先在餐巾紙的一角寫(xiě)上計(jì)數(shù)值 1 ,以表示我在使用這張餐巾紙。這時(shí),如果你也想看看我畫(huà)的藍(lán)圖,那你就要把餐巾紙上的計(jì)數(shù)值加 1 ,將它改為 2 ,這表明目前有 2 個(gè)人在同時(shí)使用這張餐巾紙(當(dāng)然,我是不會(huì)允許你用這張餐巾紙來(lái)擦鼻涕的)。你看完后,必須把計(jì)數(shù)值減 1 ,表明你對(duì)該餐巾紙的使用已經(jīng)結(jié)束。同樣,當(dāng)我將餐巾紙上的內(nèi)容全部謄寫(xiě)到筆記本上之后,我也會(huì)自覺(jué)地把餐巾紙上的計(jì)數(shù)值減 1 。此時(shí),不出意外的話,這張餐巾紙上的計(jì)數(shù)值應(yīng)當(dāng)是 0 ,它會(huì)被垃圾收集器——假設(shè)那是一個(gè)專(zhuān)門(mén)負(fù)責(zé)打掃衛(wèi)生的機(jī)器人——撿起來(lái)扔到垃圾箱里,因?yàn)槔占鞯奈┮皇姑褪钦业剿杏?jì)數(shù)值為 0 的餐巾紙并清理它們。
引用計(jì)數(shù)算法的優(yōu)點(diǎn)和缺陷同樣明顯。這一算法在執(zhí)行垃圾收集任務(wù)時(shí)速度較快,但算法對(duì)程序中每一次內(nèi)存分配和指針操作提出了額外的要求(增加或減少內(nèi)存塊的引用計(jì)數(shù))。更重要的是,引用計(jì)數(shù)算法無(wú)法正確釋放循環(huán)引用的內(nèi)存塊,對(duì)此, D. Hillis 有一段風(fēng)趣而精辟的論述:
一天,一個(gè)學(xué)生走到 Moon 面前說(shuō):“我知道如何設(shè)計(jì)一個(gè)更好的垃圾收集器了。我們必須記錄指向每個(gè)結(jié)點(diǎn)的指針數(shù)目。” Moon 耐心地給這位學(xué)生講了下面這個(gè)故事:“一天,一個(gè)學(xué)生走到 Moon 面前說(shuō):‘我知道如何設(shè)計(jì)一個(gè)更好的垃圾收集器了……’”
D. Hillis 的故事和我們小時(shí)候常說(shuō)的“從前有座山,山上有個(gè)廟,廟里有個(gè)老和尚”的故事有異曲同工之妙。這說(shuō)明,單是使用引用計(jì)數(shù)算法還不足以解決垃圾收集中的所有問(wèn)題。正因?yàn)槿绱耍糜?jì)數(shù)算法也常常被研究者們排除在狹義的垃圾收集算法之外。當(dāng)然,作為一種最簡(jiǎn)單、最直觀的解決方案,引用計(jì)數(shù)算法本身具有其不可替代的優(yōu)越性。 1980 年代前后, D. P. Friedman , D. S. Wise , H. G. Baker 等人對(duì)引用計(jì)數(shù)算法進(jìn)行了數(shù)次改進(jìn),這些改進(jìn)使得引用計(jì)數(shù)算法及其變種(如延遲計(jì)數(shù)算法等)在簡(jiǎn)單的環(huán)境下,或是在一些綜合了多種算法的現(xiàn)代垃圾收集系統(tǒng)中仍然可以一展身手。
標(biāo)記-清除( Mark-Sweep )算法
第一種實(shí)用和完善的垃圾收集算法是 J. McCarthy 等人在 1960 年提出并成功地應(yīng)用于 Lisp 語(yǔ)言的標(biāo)記-清除算法。仍以餐巾紙為例,標(biāo)記-清除算法的執(zhí)行過(guò)程是這樣的:
午餐過(guò)程中,餐廳里的所有人都根據(jù)自己的需要取用餐巾紙。當(dāng)垃圾收集機(jī)器人想收集廢舊餐巾紙的時(shí)候,它會(huì)讓所有用餐的人先停下來(lái),然后,依次詢問(wèn)餐廳里的每一個(gè)人:“你正在用餐巾紙嗎?你用的是哪一張餐巾紙?”機(jī)器人根據(jù)每個(gè)人的回答將人們正在使用的餐巾紙畫(huà)上記號(hào)。詢問(wèn)過(guò)程結(jié)束后,機(jī)器人在餐廳里尋找所有散落在餐桌上且沒(méi)有記號(hào)的餐巾紙(這些顯然都是用過(guò)的廢舊餐巾紙),把它們統(tǒng)統(tǒng)扔到垃圾箱里。
正如其名稱所暗示的那樣,標(biāo)記-清除算法的執(zhí)行過(guò)程分為“標(biāo)記”和“清除”兩大階段。這種分步執(zhí)行的思路奠定了現(xiàn)代垃圾收集算法的思想基礎(chǔ)。與引用計(jì)數(shù)算法不同的是,標(biāo)記-清除算法不需要運(yùn)行環(huán)境監(jiān)測(cè)每一次內(nèi)存分配和指針操作,而只要在“標(biāo)記”階段中跟蹤每一個(gè)指針變量的指向——用類(lèi)似思路實(shí)現(xiàn)的垃圾收集器也常被后人統(tǒng)稱為跟蹤收集器( Tracing Collector )
伴隨著 Lisp 語(yǔ)言的成功,標(biāo)記-清除算法也在大多數(shù)早期的 Lisp 運(yùn)行環(huán)境中大放異彩。盡管最初版本的標(biāo)記-清除算法在今天看來(lái)還存在效率不高(標(biāo)記和清除是兩個(gè)相當(dāng)耗時(shí)的過(guò)程)等諸多缺陷,但在后面的討論中,我們可以看到,幾乎所有現(xiàn)代垃圾收集算法都是標(biāo)記-清除思想的延續(xù),僅此一點(diǎn), J. McCarthy 等人在垃圾收集技術(shù)方面的貢獻(xiàn)就絲毫不亞于他們?cè)?Lisp 語(yǔ)言上的成就了。
復(fù)制( Copying )算法
為了解決標(biāo)記-清除算法在垃圾收集效率方面的缺陷, M. L. Minsky 于 1963 年發(fā)表了著名的論文“一種使用雙存儲(chǔ)區(qū)的 Lisp 語(yǔ)言垃圾收集器( A LISP Garbage Collector Algorithm Using Serial Secondary Storage )”。 M. L. Minsky 在該論文中描述的算法被人們稱為復(fù)制算法,它也被 M. L. Minsky 本人成功地引入到了 Lisp 語(yǔ)言的一個(gè)實(shí)現(xiàn)版本中。
復(fù)制算法別出心裁地將堆空間一分為二,并使用簡(jiǎn)單的復(fù)制操作來(lái)完成垃圾收集工作,這個(gè)思路相當(dāng)有趣。借用餐巾紙的比喻,我們可以這樣理解 M. L. Minsky 的復(fù)制算法:
餐廳被垃圾收集機(jī)器人分成南區(qū)和北區(qū)兩個(gè)大小完全相同的部分。午餐時(shí),所有人都先在南區(qū)用餐(因?yàn)榭臻g有限,用餐人數(shù)自然也將減少一半),用餐時(shí)可以隨意使用餐巾紙。當(dāng)垃圾收集機(jī)器人認(rèn)為有必要回收廢舊餐巾紙時(shí),它會(huì)要求所有用餐者以最快的速度從南區(qū)轉(zhuǎn)移到北區(qū),同時(shí)隨身攜帶自己正在使用的餐巾紙。等所有人都轉(zhuǎn)移到北區(qū)之后,垃圾收集機(jī)器人只要簡(jiǎn)單地把南區(qū)中所有散落的餐巾紙扔進(jìn)垃圾箱就算完成任務(wù)了。下一次垃圾收集的工作過(guò)程也大致類(lèi)似,惟一的不同只是人們的轉(zhuǎn)移方向變成了從北區(qū)到南區(qū)。如此循環(huán)往復(fù),每次垃圾收集都只需簡(jiǎn)單地轉(zhuǎn)移(也就是復(fù)制)一次,垃圾收集速度無(wú)與倫比——當(dāng)然,對(duì)于用餐者往返奔波于南北兩區(qū)之間的辛勞,垃圾收集機(jī)器人是決不會(huì)流露出絲毫憐憫的。
M. L. Minsky 的發(fā)明絕對(duì)算得上一種奇思妙想。分區(qū)、復(fù)制的思路不僅大幅提高了垃圾收集的效率,而且也將原本繁紛復(fù)雜的內(nèi)存分配算法變得前所未有地簡(jiǎn)明和扼要(既然每次內(nèi)存回收都是對(duì)整個(gè)半?yún)^(qū)的回收,內(nèi)存分配時(shí)也就不用考慮內(nèi)存碎片等復(fù)雜情況,只要移動(dòng)堆頂指針,按順序分配內(nèi)存就可以了),這簡(jiǎn)直是個(gè)奇跡!不過(guò),任何奇跡的出現(xiàn)都有一定的代價(jià),在垃圾收集技術(shù)中,復(fù)制算法提高效率的代價(jià)是人為地將可用內(nèi)存縮小了一半。實(shí)話實(shí)說(shuō),這個(gè)代價(jià)未免也太高了一些。
無(wú)論優(yōu)缺點(diǎn)如何,復(fù)制算法在實(shí)踐中都獲得了可以與標(biāo)記-清除算法相比擬的成功。除了 M. L. Minsky 本人在 Lisp 語(yǔ)言中的工作以外,從 1960 年代末到 1970 年代初, R. R. Fenichel 和 J. C. Yochelson 等人也相繼在 Lisp 語(yǔ)言的不同實(shí)現(xiàn)中對(duì)復(fù)制算法進(jìn)行了改進(jìn), S. Arnborg 更是成功地將復(fù)制算法應(yīng)用到了 Simula 語(yǔ)言中。
至此,垃圾收集技術(shù)的三大傳統(tǒng)算法——引用計(jì)數(shù)算法、標(biāo)記-清除算法和復(fù)制算法——都已在 1960 年前后相繼問(wèn)世,三種算法各有所長(zhǎng),也都存在致命的缺陷。從 1960 年代后期開(kāi)始,研究者的主要精力逐漸轉(zhuǎn)向?qū)@三種傳統(tǒng)算法進(jìn)行改進(jìn)或整合,以揚(yáng)長(zhǎng)避短,適應(yīng)程序設(shè)計(jì)語(yǔ)言和運(yùn)行環(huán)境對(duì)垃圾收集的效率和實(shí)時(shí)性所提出的更高要求。
走向成熟
從 1970 年代開(kāi)始,隨著科學(xué)研究和應(yīng)用實(shí)踐的不斷深入,人們逐漸意識(shí)到,一個(gè)理想的垃圾收集器不應(yīng)在運(yùn)行時(shí)導(dǎo)致應(yīng)用程序的暫停,不應(yīng)額外占用大量的內(nèi)存空間和 CPU 資源,而三種傳統(tǒng)的垃圾收集算法都無(wú)法滿足這些要求。人們必須提出更新的算法或思路,以解決實(shí)踐中碰到的諸多難題。當(dāng)時(shí),研究者的努力目標(biāo)包括:
第一,提高垃圾收集的效率。使用標(biāo)記-清除算法的垃圾收集器在工作時(shí)要消耗相當(dāng)多的 CPU 資源。早期的 Lisp 運(yùn)行環(huán)境收集內(nèi)存垃圾的時(shí)間竟占到了系統(tǒng)總運(yùn)行時(shí)間的 40% !——垃圾收集效率的低下直接造就了 Lisp 語(yǔ)言在執(zhí)行速度方面的壞名聲;直到今天,許多人還條件反射似地誤以為所有 Lisp 程序都奇慢無(wú)比。
第二,減少垃圾收集時(shí)的內(nèi)存占用。這一問(wèn)題主要出現(xiàn)在復(fù)制算法中。盡管復(fù)制算法在效率上獲得了質(zhì)的突破,但犧牲一半內(nèi)存空間的代價(jià)仍然是巨大的。在計(jì)算機(jī)發(fā)展的早期,在內(nèi)存價(jià)格以 KB 計(jì)算的日子里,浪費(fèi)客戶的一半內(nèi)存空間簡(jiǎn)直就是在變相敲詐或攔路打劫。
第三,尋找實(shí)時(shí)的垃圾收集算法。無(wú)論執(zhí)行效率如何,三種傳統(tǒng)的垃圾收集算法在執(zhí)行垃圾收集任務(wù)時(shí)都必須打斷程序的當(dāng)前工作。這種因垃圾收集而造成的延時(shí)是許多程序,特別是執(zhí)行關(guān)鍵任務(wù)的程序沒(méi)有辦法容忍的。如何對(duì)傳統(tǒng)算法進(jìn)行改進(jìn),以便實(shí)現(xiàn)一種在后臺(tái)悄悄執(zhí)行,不影響——或至少看上去不影響——當(dāng)前進(jìn)程的實(shí)時(shí)垃圾收集器,這顯然是一件更具挑戰(zhàn)性的工作。
研究者們探尋未知領(lǐng)域的決心和研究工作的進(jìn)展速度同樣令人驚奇:在 1970 年代到 1980 年代的短短十幾年中,一大批在實(shí)用系統(tǒng)中表現(xiàn)優(yōu)異的新算法和新思路脫穎而出。正是因?yàn)橛辛诉@些日趨成熟的垃圾收集算法,今天的我們才能在 Java 或 .NET 提供的運(yùn)行環(huán)境中隨心所欲地分配內(nèi)存塊,而不必?fù)?dān)心空間釋放時(shí)的風(fēng)險(xiǎn)。
標(biāo)記-整理( Mark-Compact )算法
標(biāo)記-整理算法是標(biāo)記-清除算法和復(fù)制算法的有機(jī)結(jié)合。把標(biāo)記-清除算法在內(nèi)存占用上的優(yōu)點(diǎn)和復(fù)制算法在執(zhí)行效率上的特長(zhǎng)綜合起來(lái),這是所有人都希望看到的結(jié)果。不過(guò),兩種垃圾收集算法的整合并不像 1 加 1 等于 2 那樣簡(jiǎn)單,我們必須引入一些全新的思路。 1970 年前后, G. L. Steele , C. J. Cheney 和 D. S. Wise 等研究者陸續(xù)找到了正確的方向,標(biāo)記-整理算法的輪廓也逐漸清晰了起來(lái):
在我們熟悉的餐廳里,這一次,垃圾收集機(jī)器人不再把餐廳分成兩個(gè)南北區(qū)域了。需要執(zhí)行垃圾收集任務(wù)時(shí),機(jī)器人先執(zhí)行標(biāo)記-清除算法的第一個(gè)步驟,為所有使用中的餐巾紙畫(huà)好標(biāo)記,然后,機(jī)器人命令所有就餐者帶上有標(biāo)記的餐巾紙向餐廳的南面集中,同時(shí)把沒(méi)有標(biāo)記的廢舊餐巾紙扔向餐廳北面。這樣一來(lái),機(jī)器人只消站在餐廳北面,懷抱垃圾箱,迎接撲面而來(lái)的廢舊餐巾紙就行了。
實(shí)驗(yàn)表明,標(biāo)記-整理算法的總體執(zhí)行效率高于標(biāo)記-清除算法,又不像復(fù)制算法那樣需要犧牲一半的存儲(chǔ)空間,這顯然是一種非常理想的結(jié)果。在許多現(xiàn)代的垃圾收集器中,人們都使用了標(biāo)記-整理算法或其改進(jìn)版本。
增量收集( Incremental Collecting )算法
對(duì)實(shí)時(shí)垃圾收集算法的研究直接導(dǎo)致了增量收集算法的誕生。
最初,人們關(guān)于實(shí)時(shí)垃圾收集的想法是這樣的:為了進(jìn)行實(shí)時(shí)的垃圾收集,可以設(shè)計(jì)一個(gè)多進(jìn)程的運(yùn)行環(huán)境,比如用一個(gè)進(jìn)程執(zhí)行垃圾收集工作,另一個(gè)進(jìn)程執(zhí)行程序代碼。這樣一來(lái),垃圾收集工作看上去就仿佛是在后臺(tái)悄悄完成的,不會(huì)打斷程序代碼的運(yùn)行。
在收集餐巾紙的例子中,這一思路可以被理解為:垃圾收集機(jī)器人在人們用餐的同時(shí)尋找廢棄的餐巾紙并將它們?nèi)拥嚼淅铩_@個(gè)看似簡(jiǎn)單的思路會(huì)在設(shè)計(jì)和實(shí)現(xiàn)時(shí)碰上進(jìn)程間沖突的難題。比如說(shuō),如果垃圾收集進(jìn)程包括標(biāo)記和清除兩個(gè)工作階段,那么,垃圾收集器在第一階段中辛辛苦苦標(biāo)記出的結(jié)果很可能被另一個(gè)進(jìn)程中的內(nèi)存操作代碼修改得面目全非,以至于第二階段的工作沒(méi)有辦法開(kāi)展。
M. L. Minsky 和 D. E. Knuth 對(duì)實(shí)時(shí)垃圾收集過(guò)程中的技術(shù)難點(diǎn)進(jìn)行了早期的研究, G. L. Steele 于 1975 年發(fā)表了題為“多進(jìn)程整理的垃圾收集( Multiprocessing compactifying garbage collection )”的論文,描述了一種被后人稱為“ Minsky-Knuth-Steele 算法”的實(shí)時(shí)垃圾收集算法。 E. W. Dijkstra , L. Lamport , R. R. Fenichel 和 J. C. Yochelson 等人也相繼在此領(lǐng)域做出了各自的貢獻(xiàn)。 1978 年, H. G. Baker 發(fā)表了“串行計(jì)算機(jī)上的實(shí)時(shí)表處理技術(shù)( List Processing in Real Time on a Serial Computer )”一文,系統(tǒng)闡述了多進(jìn)程環(huán)境下用于垃圾收集的增量收集算法。
增量收集算法的基礎(chǔ)仍是傳統(tǒng)的標(biāo)記-清除和復(fù)制算法。增量收集算法通過(guò)對(duì)進(jìn)程間沖突的妥善處理,允許垃圾收集進(jìn)程以分階段的方式完成標(biāo)記、清理或復(fù)制工作。詳細(xì)分析各種增量收集算法的內(nèi)部機(jī)理是一件相當(dāng)繁瑣的事情,在這里,讀者們需要了解的僅僅是: H. G. Baker 等人的努力已經(jīng)將實(shí)時(shí)垃圾收集的夢(mèng)想變成了現(xiàn)實(shí),我們?cè)僖膊挥脼槔占驍喑绦虻倪\(yùn)行而煩惱了。
分代收集( Generational Collecting )算法
和大多數(shù)軟件開(kāi)發(fā)技術(shù)一樣,統(tǒng)計(jì)學(xué)原理總能在技術(shù)發(fā)展的過(guò)程中起到強(qiáng)力催化劑的作用。 1980 年前后,善于在研究中使用統(tǒng)計(jì)分析知識(shí)的技術(shù)人員發(fā)現(xiàn),大多數(shù)內(nèi)存塊的生存周期都比較短,垃圾收集器應(yīng)當(dāng)把更多的精力放在檢查和清理新分配的內(nèi)存塊上。這個(gè)發(fā)現(xiàn)對(duì)于垃圾收集技術(shù)的價(jià)值可以用餐巾紙的例子概括如下:
如果垃圾收集機(jī)器人足夠聰明,事先摸清了餐廳里每個(gè)人在用餐時(shí)使用餐巾紙的習(xí)慣——比如有些人喜歡在用餐前后各用掉一張餐巾紙,有的人喜歡自始至終攥著一張餐巾紙不放,有的人則每打一個(gè)噴嚏就用去一張餐巾紙——機(jī)器人就可以制定出更完善的餐巾紙回收計(jì)劃,并總是在人們剛?cè)拥舨徒砑垱](méi)多久就把垃圾撿走。這種基于統(tǒng)計(jì)學(xué)原理的做法當(dāng)然可以讓餐廳的整潔度成倍提高。
D. E. Knuth , T. Knight , G. Sussman 和 R. Stallman 等人對(duì)內(nèi)存垃圾的分類(lèi)處理做了最早的研究。 1983 年, H. Lieberman 和 C. Hewitt 發(fā)表了題為“基于對(duì)象壽命的一種實(shí)時(shí)垃圾收集器( A real-time garbage collector based on the lifetimes of objects )”的論文。這篇著名的論文標(biāo)志著分代收集算法的正式誕生。此后,在 H. G. Baker , R. L. Hudson , J. E. B. Moss 等人的共同努力下,分代收集算法逐漸成為了垃圾收集領(lǐng)域里的主流技術(shù)。
分代收集算法通常將堆中的內(nèi)存塊按壽命分為兩類(lèi),年老的和年輕的。垃圾收集器使用不同的收集算法或收集策略,分別處理這兩類(lèi)內(nèi)存塊,并特別地把主要工作時(shí)間花在處理年輕的內(nèi)存塊上。分代收集算法使垃圾收集器在有限的資源條件下,可以更為有效地工作——這種效率上的提高在今天的 Java 虛擬機(jī)中得到了最好的證明。
應(yīng)用浪潮
Lisp 是垃圾收集技術(shù)的第一個(gè)受益者,但顯然不是最后一個(gè)。在 Lisp 語(yǔ)言之后,許許多多傳統(tǒng)的、現(xiàn)代的、后現(xiàn)代的語(yǔ)言已經(jīng)把垃圾收集技術(shù)拉入了自己的懷抱。隨便舉幾個(gè)例子吧:誕生于 1964 年的 Simula 語(yǔ)言, 1969 年的 Smalltalk 語(yǔ)言, 1970 年的 Prolog 語(yǔ)言, 1973 年的 ML 語(yǔ)言, 1975 年的 Scheme 語(yǔ)言, 1983 年的 Modula-3 語(yǔ)言, 1986 年的 Eiffel 語(yǔ)言, 1987 年的 Haskell 語(yǔ)言……它們都先后使用了自動(dòng)垃圾收集技術(shù)。當(dāng)然,每一種語(yǔ)言使用的垃圾收集算法可能不盡相同,大多數(shù)語(yǔ)言和運(yùn)行環(huán)境甚至同時(shí)使用了多種垃圾收集算法。但無(wú)論怎樣,這些實(shí)例都說(shuō)明,垃圾收集技術(shù)從誕生的那一天起就不是一種曲高和寡的“學(xué)院派”技術(shù)。
對(duì)于我們熟悉的 C 和 C++ 語(yǔ)言,垃圾收集技術(shù)一樣可以發(fā)揮巨大的功效。正如我們?cè)趯W(xué)校中就已經(jīng)知道的那樣, C 和 C++ 語(yǔ)言本身并沒(méi)有提供垃圾收集機(jī)制,但這并不妨礙我們?cè)诔绦蛑惺褂镁哂欣占δ艿暮瘮?shù)庫(kù)或類(lèi)庫(kù)。例如,早在 1988 年, H. J. Boehm 和 A. J. Demers 就成功地實(shí)現(xiàn)了一種使用保守垃圾收集算法( Conservative GC Algorithmic )的函數(shù)庫(kù)。我們可以在 C 語(yǔ)言或 C++ 語(yǔ)言中使用該函數(shù)庫(kù)完成自動(dòng)垃圾收集功能,必要時(shí),甚至還可以讓傳統(tǒng)的 C/C++ 代碼與使用自動(dòng)垃圾收集功能的 C/C++ 代碼在一個(gè)程序里協(xié)同工作。
1995 年誕生的 Java 語(yǔ)言在一夜之間將垃圾收集技術(shù)變成了軟件開(kāi)發(fā)領(lǐng)域里最為流行的技術(shù)之一。從某種角度說(shuō),我們很難分清究竟是 Java 從垃圾收集中受益,還是垃圾收集技術(shù)本身借 Java 的普及而揚(yáng)名。值得注意的是,不同版本的 Java 虛擬機(jī)使用的垃圾收集機(jī)制并不完全相同, Java 虛擬機(jī)其實(shí)也經(jīng)過(guò)了一個(gè)從簡(jiǎn)單到復(fù)雜的發(fā)展過(guò)程。在 Java 虛擬機(jī)的 1.4.1 版中,人們可以體驗(yàn)到的垃圾收集算法就包括分代收集、復(fù)制收集、增量收集、標(biāo)記-整理、并行復(fù)制( Parallel Copying )、并行清除( Parallel Scavenging )、并發(fā)( Concurrent )收集等許多種, Java 程序運(yùn)行速度的不斷提升在很大程度上應(yīng)該歸功于垃圾收集技術(shù)的發(fā)展與完善。
盡管歷史上已經(jīng)有許多包含垃圾收集技術(shù)的應(yīng)用平臺(tái)和操作系統(tǒng)出現(xiàn),但 Microsoft .NET 卻是第一種真正實(shí)用化的、包含了垃圾收集機(jī)制的通用語(yǔ)言運(yùn)行環(huán)境。事實(shí)上, .NET 平臺(tái)上的所有語(yǔ)言,包括 C# 、 Visual Basic .NET 、 Visual C++ .NET 、 J# 等等,都可以通過(guò)幾乎完全相同的方式使用 .NET 平臺(tái)提供的垃圾收集機(jī)制。我們似乎可以斷言, .NET 是垃圾收集技術(shù)在應(yīng)用領(lǐng)域里的一次重大變革,它使垃圾收集技術(shù)從一種單純的技術(shù)變成了應(yīng)用環(huán)境乃至操作系統(tǒng)中的一種內(nèi)在文化。這種變革對(duì)未來(lái)軟件開(kāi)發(fā)技術(shù)的影響力也許要遠(yuǎn)遠(yuǎn)超過(guò) .NET 平臺(tái)本身的商業(yè)價(jià)值。
大勢(shì)所趨
今天,致力于垃圾收集技術(shù)研究的人們?nèi)栽诓恍概Γ麄兊难芯糠较虬ǚ植际较到y(tǒng)的垃圾收集、復(fù)雜事務(wù)環(huán)境下的垃圾收集、數(shù)據(jù)庫(kù)等特定系統(tǒng)的垃圾收集等等。
但在程序員中間,仍有不少人對(duì)垃圾收集技術(shù)不屑一顧,他們寧愿相信自己逐行編寫(xiě)的 free 或 delete 命令,也不愿把垃圾收集的重任交給那些在他們看來(lái)既蠢又笨的垃圾收集器。
我個(gè)人認(rèn)為,垃圾收集技術(shù)的普及是大勢(shì)所趨,這就像生活會(huì)越來(lái)越好一樣毋庸置疑。今天的程序員也許會(huì)因?yàn)槔占饕加靡欢ǖ?CPU 資源而對(duì)其望而卻步,但二十多年前的程序員還曾因?yàn)楦呒?jí)語(yǔ)言速度太慢而堅(jiān)持用機(jī)器語(yǔ)言寫(xiě)程序呢!在硬件速度日新月異的今天,我們是要吝惜那一點(diǎn)兒時(shí)間損耗而踟躇不前,還是該堅(jiān)定不移地站在代碼和運(yùn)行環(huán)境的凈化劑——垃圾收集的一邊呢?
常用的內(nèi)存管理方法有哪幾種
下一篇:唱戲內(nèi)存卡如何解密?