亚洲狠狠干,亚洲国产福利精品一区二区,国产八区,激情文学亚洲色图

一種計(jì)算機(jī)自動(dòng)內(nèi)存管理的系統(tǒng)及方法

文檔序號(hào):6555910閱讀:338來源:國(guó)知局
專利名稱:一種計(jì)算機(jī)自動(dòng)內(nèi)存管理的系統(tǒng)及方法
技術(shù)領(lǐng)域
本發(fā)明涉及計(jì)算機(jī)內(nèi)存管理技術(shù),更具體地說,涉及一種尤其適用于面向?qū)ο蟮木幊涕_發(fā)系統(tǒng),實(shí)現(xiàn)精確的、確定性回收的、無暫停的、高效的內(nèi)存垃圾回收的系統(tǒng)和方法。
背景技術(shù)
對(duì)于計(jì)算系統(tǒng)的運(yùn)行效率和可靠性來說,內(nèi)存的管理是相當(dāng)重要的。自動(dòng)內(nèi)存管理(Automatic MemoryManagement)技術(shù)或者說自動(dòng)存儲(chǔ)回收(Automatic Storage Reclamation)技術(shù)將程序員從繁瑣的手工內(nèi)存管理中釋放出來,并同時(shí)減少了程序員犯錯(cuò)誤的可能性,提高了系統(tǒng)運(yùn)行的可靠性。
沒有自動(dòng)內(nèi)存管理之前,程序員需要顯式地通過free或者delete等類似手段回收那些位于堆(Heap)中的內(nèi)存塊(或者稱為對(duì)象),忘記回收將會(huì)造成內(nèi)存的浪費(fèi)并影響程序運(yùn)行的效率,相反如果錯(cuò)誤地回收那些正在使用中的內(nèi)存,則會(huì)帶來災(zāi)難性的后果,導(dǎo)致程序崩潰。自動(dòng)內(nèi)存管理技術(shù)通過計(jì)算機(jī)程序自動(dòng)找出不再使用的內(nèi)存塊,釋放它們,令應(yīng)用程序可以再次利用這些內(nèi)存。通常人們將這些不會(huì)被應(yīng)用程序使用的內(nèi)存塊稱為垃圾(Garbage);而那些正在使用中或者可能被使用的內(nèi)存稱為活動(dòng)的(Live);錯(cuò)誤指向已經(jīng)回收了的內(nèi)存的指針稱為懸掛指針(dangling pointer)。
自動(dòng)內(nèi)存管理,或者說垃圾回收器(Garbage Collector),有很多的實(shí)現(xiàn)方式,主要包括兩大類引用計(jì)數(shù)(Reference Counting)和引用跟蹤回收(Reference Tracing Collecting)。
采用引用計(jì)數(shù)的垃圾回收器,系統(tǒng)為堆上每一個(gè)對(duì)象都維護(hù)一個(gè)計(jì)數(shù)器,當(dāng)一個(gè)對(duì)象被創(chuàng)建并且被引用時(shí),這個(gè)計(jì)數(shù)就被置為1。當(dāng)有新的變量引用該對(duì)象,計(jì)數(shù)器進(jìn)行自加運(yùn)算。當(dāng)一個(gè)引用超出作用范圍或者被賦予新值的時(shí)候,計(jì)數(shù)器進(jìn)行自減運(yùn)算。引用計(jì)數(shù)為0的對(duì)象,會(huì)被作為垃圾回收。引用計(jì)數(shù)的運(yùn)行時(shí)的開銷比較大,更致命的是如果垃圾對(duì)象之間相互循環(huán)引用,則引用計(jì)數(shù)方法就無法回收這些對(duì)象,因此這些年來,引用計(jì)數(shù)對(duì)于嚴(yán)格的垃圾回收器設(shè)計(jì)者來說吸引力不大,常常排除在狹義的垃圾收集算法之外。但是,作為另一方面,第一,引用計(jì)數(shù)可以在內(nèi)存變成垃圾的那一時(shí)刻,就立即將內(nèi)存回收,這一特性稱為確定性地回收(Deterministic Reclamation),而這往往是其他回收方法所缺乏的;第二,由于引用計(jì)數(shù)的維護(hù)工作總是交織在應(yīng)用程序的執(zhí)行過程中,維護(hù)所造成的管理開銷的粒度小、時(shí)間短,不會(huì)長(zhǎng)時(shí)間地中斷應(yīng)用程序運(yùn)行,比較適合實(shí)時(shí)(Real-Time)性要求高的場(chǎng)合。
采用引用跟蹤方法的垃圾回收器,總的來說是通過跟蹤對(duì)象之間的相互引用關(guān)系,從根集(Root Set)開始掃描,確定根集中的指針,然后以這些指針為起點(diǎn),跟蹤遍歷所有可達(dá)的對(duì)象,在遍歷過程中遇到的對(duì)象,就被標(biāo)記為活動(dòng)。當(dāng)遍歷完成以后,那些沒有被標(biāo)記的對(duì)象,就被作為垃圾回收了。根集的定義是指可以被應(yīng)用程序直接訪問,而無須通過指針間接訪問的數(shù)據(jù)。具體的說,是指應(yīng)用程序可以直接訪問的指針變量的集合(包括局部變量、參數(shù)、類變量)。引用跟蹤回收方法中,最基本是標(biāo)記-清掃(Mark andSweep)方法,其他還有標(biāo)記-合并(Mark-Compact)等變種。相對(duì)引用計(jì)數(shù)回收方法,引用跟蹤回收方法的最大優(yōu)勢(shì)是它能夠正確地回收所有的垃圾對(duì)象,包括循環(huán)引用的垃圾。隨著Java語言的出現(xiàn)和發(fā)展,采用引用跟蹤方法的垃圾回收器越來越流行,甚至成為垃圾回收GC(Garbage Collection)的同義詞。其中一個(gè)典型的例子就是微軟公司使用引用計(jì)數(shù)機(jī)制的COM體系,正在逐步被拋棄,而轉(zhuǎn)向使用引用跟蹤方法的垃圾回收機(jī)制的.NET平臺(tái)。
盡管引用跟蹤的垃圾回收機(jī)制從首先提出到現(xiàn)在已經(jīng)有40多年的歷史,其間有大量的改進(jìn)的實(shí)現(xiàn)方法申請(qǐng)了專利,但是距離一個(gè)理想的垃圾回收器還有相當(dāng)?shù)木嚯x。幾個(gè)主要的基本問題始終沒有得到徹底的解決,一直困擾著人們,隨著GC的越來越廣泛的流行,這些問題的解決越發(fā)顯得迫切。我們可以從微軟公司這些年來不斷改變GC的接口、引入新的GC概念,略見一斑。這些問題包括垃圾收集導(dǎo)致應(yīng)用程序的暫停、垃圾收集產(chǎn)生的運(yùn)行開銷、內(nèi)存的利用率低導(dǎo)致對(duì)性能的影響、和不確定地回收的垃圾對(duì)象等等。下面,對(duì)于本發(fā)明所要解決的GC的主要問題作一簡(jiǎn)單地描述。
(1)非確定的垃圾對(duì)象回收該問題是指人們無法確定某個(gè)垃圾對(duì)象何時(shí)被系統(tǒng)回收,從而導(dǎo)致與資源管理RAII(Resource Acquisition Is Initialization)的核心內(nèi)容相沖突。
引用跟蹤的垃圾回收,(以后簡(jiǎn)稱為垃圾回收或GC,它不包括引用計(jì)數(shù)的方法;使用自動(dòng)內(nèi)存管理來泛指含引用計(jì)數(shù)的垃圾回收方法),系統(tǒng)在進(jìn)行引用遍歷的過程是相當(dāng)耗費(fèi)資源的,包括耗費(fèi)處理器CPU資源和遍歷導(dǎo)致的內(nèi)存緩沖失效的開銷,后者尤其重要,而且該操作的復(fù)雜程度正比于系統(tǒng)中的活動(dòng)對(duì)象而不是垃圾對(duì)象的個(gè)數(shù),所以即使已經(jīng)沒有垃圾對(duì)象了,執(zhí)行回收操作仍然帶來很大的開銷。所以回收操作不能經(jīng)常執(zhí)行,造成在兩次回收操作之間變成垃圾的對(duì)象不能及時(shí)回收。分代回收(GenerationCollection)和并發(fā)回收(Concurrent Collection)等改進(jìn)方法提高了垃圾回收的頻率,但是仍不能保證對(duì)象的及時(shí)回收,因?yàn)樵谝淮伪闅v過程中變成垃圾的對(duì)象極有可能仍需等待下一次的回收的時(shí)候,才能得到真正的回收,詳細(xì)說明參見本發(fā)明實(shí)施例中的解釋。此外,常規(guī)的GC實(shí)現(xiàn)例如微軟的.NET平臺(tái)和Sun公司的Java,通常只有在內(nèi)存緊張的時(shí)候才進(jìn)行一次完整的(非分代的)垃圾回收,如果運(yùn)行的機(jī)器有足夠多的內(nèi)存,則某些垃圾對(duì)象在程序結(jié)束之前永遠(yuǎn)不會(huì)被回收。
資源管理RAII的理念是C++等主流開發(fā)語言所倡導(dǎo)的,其核心內(nèi)容是把資源托管給對(duì)象,用對(duì)象代表資源。資源的申請(qǐng)和釋放很自然地和對(duì)象的構(gòu)造與析構(gòu)操作對(duì)應(yīng)起來,在對(duì)象的構(gòu)造過程中申請(qǐng)并獲得資源,而在對(duì)象的析構(gòu)過程中釋放資源,析構(gòu)過程在對(duì)象被回收時(shí)執(zhí)行。資源的定義很廣泛,包括打開的文件、網(wǎng)絡(luò)連接、軟件許可證、圖形界面的窗口,…,等,通常可以理解為個(gè)數(shù)相當(dāng)有限的、而對(duì)該類資源的請(qǐng)求的競(jìng)爭(zhēng)又相當(dāng)劇烈的對(duì)象。這些資源對(duì)象要求在使用完后,盡快地釋放。而這一點(diǎn)恰恰是垃圾回收所欠缺的,對(duì)象的回收總是不確定的、不及時(shí)的,特別是在一些特殊的場(chǎng)合,對(duì)象之間不存在循環(huán)引用的關(guān)系,例如軟件模塊的動(dòng)態(tài)加載和卸載過程,更加希望系統(tǒng)能夠象引用計(jì)數(shù)那樣支持確定的析構(gòu)過程的執(zhí)行,包括有次序的執(zhí)行這些析構(gòu)函數(shù)。
由于系統(tǒng)不支持確定的對(duì)象回收和析構(gòu),給應(yīng)用程序的設(shè)計(jì)和編制帶來了很多不便。Java引入弱引用(weak reference)來輔助的終結(jié)(finalization)函數(shù)的執(zhí)行,.NET引入Dispose成員函數(shù)顯式地釋放對(duì)象所關(guān)聯(lián)的資源,最進(jìn)又引入Destructor函數(shù)和棧內(nèi)析構(gòu)的概念,目的都是為了盡量減少GC機(jī)制帶來的不便。但是,由于垃圾回收核心并沒有大的改進(jìn),這些提供給應(yīng)用程序的輔助手段并沒有太大作用,程序員被迫顯式地、即手工地管理對(duì)象的資源釋放,容易發(fā)生忘記釋放或提前釋放資源錯(cuò)誤,破壞了數(shù)據(jù)一致性,造成系統(tǒng)的不穩(wěn)定。在某些有眾多資源相互依賴的情況下,手工確定哪些資源需要釋放相當(dāng)困難,而且資源的依賴性有傳遞的特性,一個(gè)深藏的、未被注意的對(duì)象可能間接地經(jīng)過多個(gè)其他對(duì)象,依賴某個(gè)資源,如果錯(cuò)誤地提前釋放這些資源,將導(dǎo)致將來的程序執(zhí)行失敗。所以由于不支持確定性的析構(gòu),妥協(xié)的改進(jìn)只是令事情簡(jiǎn)單的更簡(jiǎn)單、復(fù)雜的更復(fù)雜,將維護(hù)對(duì)象之間的依賴關(guān)系推回給了應(yīng)用程序。
本發(fā)明的一個(gè)實(shí)施例總是立即釋放沒有引用的對(duì)象,因此資源的釋放可以由系統(tǒng)自動(dòng)完成。
(2)內(nèi)存的利用率低Java、.NET編制的程序,在運(yùn)行時(shí)占內(nèi)存比較多,這是一個(gè)不爭(zhēng)的事實(shí)。其原因很多,其中一個(gè)就是激發(fā)垃圾回收操作的條件,為了避免GC操作帶來的巨大開銷,系統(tǒng)通常僅在需要的時(shí)候,即內(nèi)存不足的情況下才執(zhí)行完整的回收操作,而僅僅一個(gè)未及時(shí)回收的對(duì)象就可能引用大量其他對(duì)象,占據(jù)大量的內(nèi)存。一個(gè)沒有被引用的對(duì)象在GC環(huán)境中并不能立即釋放,必須依賴GC遍歷操作來檢測(cè)確定,所以在兩次GC操作其間,內(nèi)存只是分配而不會(huì)釋放,內(nèi)存使用量總是不斷增加,直到GC操作才恢復(fù)到真正的、必須的使用值。也就是說,內(nèi)存的使用量總是鋸尺形狀的變化的,而且總是大于等于實(shí)際的必要使用量,在GC操作前浪費(fèi)的內(nèi)存達(dá)到最大值。
分代垃圾回收方法及變種,只是盡快地回收了一部分的垃圾,其回收能力隨應(yīng)用程序的不同變化很大,除非最老一代的垃圾得到回收,才真正釋放了不需要的內(nèi)存。
本發(fā)明則隨時(shí)釋放引用數(shù)為0的對(duì)象,也就是說在沒有循環(huán)引用的情況下,本系統(tǒng)沒有多余的內(nèi)存占用;而在有循環(huán)引用的情況下,多余占用的內(nèi)存僅僅是這些循環(huán)引用的內(nèi)存,并且在GC回收操作時(shí)全部回收,大大提高了內(nèi)存利用率。
(3)導(dǎo)致應(yīng)用程序暫停到目前為止,引用跟蹤垃圾回收操作總是不可避免地造成或多或少的應(yīng)用程序暫停。有很多自稱無暫停的垃圾回收系統(tǒng)是指用戶感覺不到暫停,例如,暫停小于1毫秒的、面向多媒體系統(tǒng)的垃圾回收器。它們基本上都是使用增量(Incremental)回收方法及變種,無法保證或者預(yù)測(cè)最壞情況下會(huì)造成應(yīng)用系統(tǒng)多長(zhǎng)時(shí)間的暫停,暫停的長(zhǎng)短取決于特定的應(yīng)用程序,包括的對(duì)象的使用情況、垃圾回收時(shí)的應(yīng)用程序的執(zhí)行情況、回收的速度與引用關(guān)系變化的速度等等。這些系統(tǒng)的暫停時(shí)間大多是通過實(shí)際測(cè)試得出的,并沒有理論上的最壞情況的保證,所以通常被稱為軟實(shí)時(shí)的系統(tǒng)。在后面的實(shí)施例描述中,詳細(xì)分析了現(xiàn)有技術(shù)導(dǎo)致暫停的原因,主要是安全點(diǎn)(GC-Safe)及根集掃描等。
暫停導(dǎo)致的一個(gè)主要問題是暫停相關(guān)線程是異步執(zhí)行的,而且沒有優(yōu)先級(jí)別的概念,而且需要暫停所有的線程以保證取得引用關(guān)系的一個(gè)數(shù)據(jù)一致的快照。也就是說,執(zhí)行垃圾回收操作的線程有可能需要阻塞高優(yōu)先級(jí)的線程而等待低優(yōu)先級(jí)別的線程,所以有暫停的垃圾回收系統(tǒng)很難在要求線程迅速作出響應(yīng)的系統(tǒng)中應(yīng)用,例如操作系統(tǒng)內(nèi)核。
本發(fā)明的一個(gè)實(shí)施例徹底地去除了垃圾回收操作造成應(yīng)用程序暫停。
(4)C++環(huán)境下很難高效、正確地精確回收引用跟蹤回收的精確性,主要是指系統(tǒng)能夠精確標(biāo)記活動(dòng)的對(duì)象。這是保證完全地回收所有廢棄對(duì)象的前提,否則就可能造成內(nèi)存泄漏。C++環(huán)境下,編譯器未能提供有效的根集指針分布情況,特別是線程運(yùn)行棧內(nèi)的指針分布。因此,比較流行的方法是使用保守的(conservative)回收方式,它將類似指針的數(shù)據(jù)都當(dāng)作有效的指針進(jìn)行引用遍歷。缺點(diǎn)是不能將所有的垃圾都全部回收,也不能回收隱藏的指針,例如位域(bit-field)和聯(lián)合(Union),一個(gè)錯(cuò)誤保留下來未被回收的對(duì)象可能引用大量其他的對(duì)象或者引用大塊的內(nèi)存,因此造成大量的內(nèi)存泄露。而且,某些編譯器進(jìn)行的優(yōu)化處理可能導(dǎo)致某些指針即使是保守的猜測(cè)也不能檢測(cè)到,產(chǎn)生GC-Unsafe的代碼點(diǎn),如果在這些代碼點(diǎn)進(jìn)行垃圾回收,則有可能造成錯(cuò)誤地回收活動(dòng)的對(duì)象,造成災(zāi)難性的系統(tǒng)崩潰。其他一些實(shí)現(xiàn)方式,例如使用各式各樣的數(shù)據(jù)結(jié)構(gòu),將根集中的指針變量邏輯上匯聚在一起,例如使用單鏈表的美國(guó)專利US 6,907,437 B1,或者使用指針句柄數(shù)組的方法等。這些方法面向的是根集中的指針變量,由于根集的變量訪問相當(dāng)頻繁,而且是編譯器優(yōu)化的重點(diǎn)目標(biāo),它們的缺點(diǎn)是開銷太大,而且有些設(shè)計(jì)同樣存在保守回收的GC-Unsafe問題,所以在業(yè)界很少見到相關(guān)的應(yīng)用。參考Hans-J.Boehm的conservative garbagecollection及《A Proposal for Garbage-Collector-Safe C Compilation》一文。
本發(fā)明的基礎(chǔ)就是無需編譯器的特殊支持,實(shí)現(xiàn)精確的引用跟蹤回收。
(5)引用計(jì)數(shù)和引用跟蹤很難合并在一起很早以前人們就開始考慮能否將引用計(jì)數(shù)和引用跟蹤組合起來、取長(zhǎng)補(bǔ)短,但很可惜,都不成功。主要的原因是這兩者的開銷都很大,而且引用跟蹤有很多各式各樣的缺點(diǎn),簡(jiǎn)單的合并往往造成缺點(diǎn)的疊加,而且兩者有可能同時(shí)回收同一個(gè)對(duì)象,如何同步這些操作,將帶來新問題,最終因?yàn)榇鷥r(jià)太大,被很多人所否定。Brian Harry就曾就此問題以Resource Management為題在網(wǎng)上展開了有關(guān)的討論,可以作為參考。
本發(fā)明的一個(gè)實(shí)施例成功地將引用計(jì)數(shù)和引用跟蹤結(jié)合在一起,并將運(yùn)行開銷降到比常規(guī)的引用計(jì)數(shù)還要小很多。
有關(guān)垃圾回收的各種基本方法可以參考Paul R.Wilson的《Uniprocessor Garbage CollectionTechniques》一文。
上述的C++語言僅僅是一種典型代表,其實(shí)質(zhì)是指那些編譯器不提供垃圾回收信息的開發(fā)環(huán)境。

發(fā)明內(nèi)容
針對(duì)上述目前的自動(dòng)內(nèi)存管理中存在的問題,本發(fā)明的目的在于提供一種在公知計(jì)算機(jī)系統(tǒng)內(nèi)部運(yùn)行的自動(dòng)內(nèi)存管理的系統(tǒng)和方法。通過本發(fā)明解決了目前的垃圾回收機(jī)制不能提供確定性回收所引發(fā)的一系列問題;徹底地解決了垃圾回收造成應(yīng)用程序暫停的問題;擴(kuò)大了垃圾回收機(jī)制的使用范圍,包括在C++的開發(fā)環(huán)境下、在不支持線程掛起的環(huán)境下、甚至在操作系統(tǒng)內(nèi)核中。
本發(fā)明通過在應(yīng)用程序運(yùn)行時(shí)動(dòng)態(tài)標(biāo)識(shí)部分受管理的對(duì)象,這些對(duì)象被來自擴(kuò)展根集的指針?biāo)茫谙到y(tǒng)進(jìn)行垃圾回收操作的時(shí)候,首先確定了這些被標(biāo)識(shí)出來的對(duì)象,然后以它們作為跟蹤遍歷的起點(diǎn),進(jìn)行引用跟蹤,及其他剩余工作。其主要特點(diǎn)是避開了目前的根集掃描操作,這樣所有的應(yīng)用程序就自然地成為GC-Safe的代碼,可以隨時(shí)被中斷,垃圾回收操作可以在任意執(zhí)行點(diǎn)進(jìn)行,成為無暫停增量回收的基礎(chǔ)之一;不再需要編譯器的特殊支持,可以在C++為代表的語言環(huán)境中完成精確的引用跟蹤回收;動(dòng)態(tài)地標(biāo)識(shí)擴(kuò)展根集引用的對(duì)象,為對(duì)象的動(dòng)態(tài)立即回收打下基礎(chǔ),使引用計(jì)數(shù)和引用跟蹤兩種回收方法可以有機(jī)地結(jié)合在一起。
本發(fā)明的一個(gè)實(shí)施例采用引用計(jì)數(shù)的方式對(duì)上述對(duì)象進(jìn)行標(biāo)識(shí),稱為鎖定計(jì)數(shù),并維護(hù)一個(gè)代表其他受管理對(duì)象的引用個(gè)數(shù)的引用計(jì)數(shù)器,當(dāng)兩個(gè)引用計(jì)數(shù)器的值均為0的時(shí)候,立即回收該對(duì)象。在適當(dāng)?shù)臅r(shí)候,進(jìn)行引用跟蹤回收操作,回收循環(huán)引用的垃圾對(duì)象。如果應(yīng)用程序精心設(shè)計(jì),消除了循環(huán)引用的數(shù)據(jù)結(jié)構(gòu),則垃圾回收操作可以無須進(jìn)行,這樣就提供了一種優(yōu)化手段給予程序開發(fā)者。對(duì)于一個(gè)受管理的對(duì)象,通過區(qū)分來自不同區(qū)域的、不同功效的指針,采用不同的處理方法,其中對(duì)于線程運(yùn)行棧內(nèi)的指針,包括函數(shù)參數(shù)、自動(dòng)變量、返回值,采用一種特殊的處理方法,避免了引用計(jì)數(shù)器的維護(hù)操作,大大降低了整體的引用計(jì)數(shù)所產(chǎn)生的開銷。該實(shí)施例解決了資源管理RAII和垃圾回收之間的矛盾,統(tǒng)一回收對(duì)象及資源,用戶只需在析構(gòu)函數(shù)中編寫資源釋放的代碼,系統(tǒng)便自動(dòng)在適當(dāng)?shù)臅r(shí)候釋放相關(guān)資源,無須用戶手工參與,簡(jiǎn)化的應(yīng)用程序的體系設(shè)計(jì),提高了應(yīng)用程序的可靠性和穩(wěn)定性。
在避開根集掃描的基礎(chǔ)上,改進(jìn)了垃圾回收方法,在引用計(jì)數(shù)維護(hù)代碼中加入寫屏障(Write Barrier)的相關(guān)處理,改進(jìn)了3色或2色的增量垃圾回收方法,允許應(yīng)用程序不斷修改引用關(guān)系的同時(shí),確保跟蹤遍歷能夠及時(shí)正確完成,從而徹底地消除了垃圾回收導(dǎo)致應(yīng)用程序的暫停。發(fā)明體現(xiàn)在跟蹤遍歷的過程抽象成為以下步驟(1)將所有對(duì)象從“黑”色切換成“白”色,并初始化其它的相關(guān)數(shù)據(jù);(2)將被應(yīng)用程序標(biāo)識(shí)的鎖定對(duì)象轉(zhuǎn)成“灰”色,并開始跟蹤遍歷,將“灰”色的對(duì)象轉(zhuǎn)成“黑”色;在此其間,應(yīng)用程序可以不斷地改變引用關(guān)系,這些改變被立即捕獲而不是之后的某個(gè)時(shí)刻,賦值操作導(dǎo)致相關(guān)的對(duì)象從“白”變“灰”,并被系統(tǒng)跟蹤遍歷;新創(chuàng)建的對(duì)象則直接變“黑”;刪除的對(duì)象可以立即刪除或者延遲刪除;(3)步驟2本身保證了它一定能夠結(jié)束,一旦發(fā)生沒有了“灰”色對(duì)象,即可結(jié)束跟蹤遍歷的過程。通過這樣的方法,消除了目前增量回收方法要求全部線程到達(dá)一個(gè)數(shù)據(jù)一致的狀態(tài)下,才能夠判斷跟蹤遍歷的是否可以結(jié)束,消除了始終必須暫停所有線程來檢查全局的數(shù)據(jù)的可達(dá)性。(說明黑、白、灰三種顏色為增量回收方法中的常用表述,僅邏輯上的代表,不意味需要存在實(shí)際上的標(biāo)志)。
這些發(fā)明可以綜合使用,也可以單獨(dú)使用或者配合其他方法使用,特別是增量回收方法的改進(jìn),在滿足一定的必要條件下,可以輕易地整合到將來的其他的回收方法中。一個(gè)整合了上述方法的自動(dòng)內(nèi)存管理器有這樣的優(yōu)勢(shì)可以運(yùn)行于更加廣泛的平臺(tái)上,包括C++語言環(huán)境下,提供精確的引用跟蹤回收,支持隱藏的指針,以及對(duì)本地對(duì)象的跟蹤;提供統(tǒng)一的資源管理和對(duì)象管理,確定性的對(duì)象回收和析構(gòu)過程調(diào)用;提供徹底無暫停的增量垃圾回收,可預(yù)測(cè)最壞情況,適用于要求實(shí)時(shí)響應(yīng)的操作系統(tǒng)內(nèi)核;提供高利用率的內(nèi)存管理,任何時(shí)候僅僅是循環(huán)引用的垃圾對(duì)象浪費(fèi)了內(nèi)存,精心設(shè)計(jì)的應(yīng)用程序可以完全不需要垃圾回收操作;運(yùn)行時(shí)的管理開銷遠(yuǎn)低遠(yuǎn)于常規(guī)的引用計(jì)數(shù)方法,配合減少了垃圾回收的次數(shù)和利用了C++語言的執(zhí)行效率,系統(tǒng)的整體效率相當(dāng)令人滿意。


通過以下借助附圖的詳細(xì)描述,將會(huì)更容易地理解本發(fā)明,其中圖1是一個(gè)根據(jù)本發(fā)明的并入自動(dòng)內(nèi)存管理能力的計(jì)算機(jī)系統(tǒng)的方框圖;圖2是圖1中自動(dòng)內(nèi)存管理系統(tǒng)的實(shí)施例一的方框圖;圖3A是一個(gè)受管理對(duì)象的結(jié)構(gòu)示意圖;圖3B是一個(gè)受管理對(duì)象的示意圖;圖3C是一個(gè)受管理對(duì)象的引用關(guān)系示意圖;圖4是一個(gè)“Stop-The-World”的引用跟蹤的垃圾回收方法的流程圖;圖5A是賦值操作開始之前的初始狀態(tài)的示意圖;圖5B是賦值操作之后的示意圖;圖5C是賦值操作之后,且原來的引用失效之后的示意圖;圖6是原來的應(yīng)用程序偽代碼;圖7是對(duì)圖6進(jìn)行修改的部分;圖8是修改之后的應(yīng)用程序偽代碼;圖9A是返回對(duì)象指針的操作發(fā)生之前的初始狀態(tài)示意圖;圖9B是返回對(duì)象指針的操作發(fā)生之后的狀態(tài)示意圖;圖10A是一個(gè)增量垃圾回收的流程圖;圖10B是圖10A中確定鎖定對(duì)象的詳細(xì)流程圖。
圖11是自動(dòng)內(nèi)存管理系統(tǒng)實(shí)施例二的方框圖;圖12A是增量回收的標(biāo)記階段的流程圖;圖12B是增量回收的對(duì)象引用關(guān)系的示意圖;圖13是實(shí)施例二的增量跟蹤回收器的標(biāo)記階段的內(nèi)部數(shù)據(jù)方框圖;圖14是實(shí)施例二的增量回收操作的標(biāo)記階段的流程圖;圖15是實(shí)施例二的賦值操作的流程圖;圖16是實(shí)施例二的創(chuàng)建新對(duì)象的流程圖;具體實(shí)施方式
術(shù)語“對(duì)象”用在本文中是指一個(gè)C++的自定義結(jié)構(gòu),或者一個(gè)動(dòng)態(tài)分配的內(nèi)存塊;術(shù)語“引用”用在本文中是指一個(gè)對(duì)象的地址被另一個(gè)變量,特別是指針變量所使用,通過該地址,可以訪問該對(duì)象的成員數(shù)據(jù)和調(diào)用該對(duì)象的成員函數(shù)。
術(shù)語“指針”和“引用”在大多數(shù)情況下可以互換,不過有的時(shí)候“指針”多用于占一定內(nèi)存空間的變量,而“引用”多僅僅是對(duì)象的地址的數(shù)值?;蛘哒f指針通常指C語言中的“左值”,而引用通常指C語言的“右值”。
術(shù)語“左值”和“右值”與C/C++的定義相同,來源于賦值操作等號(hào)左右的表達(dá)式的類型。左值通常有確定的地址,而右值是一個(gè)數(shù)值。
術(shù)語“白”、“黑”和“灰”,在本文中按照引用跟蹤垃圾回收的慣例,分別代表不可達(dá)對(duì)象或尚未遍歷的對(duì)象、已經(jīng)完成了遍歷的活動(dòng)對(duì)象、及尚未執(zhí)行遍歷操作的活動(dòng)對(duì)象。
術(shù)語“集合”、“常規(guī)集合技術(shù)”和“集合管理手段”,在本文中是指在邏輯上實(shí)現(xiàn)集合的基本操作的數(shù)據(jù)結(jié)構(gòu),包括各種鏈表(List)、數(shù)組(Array)、向量(Vector)、聚集(Collection)、集合(Set)、圖(map)、哈希表(Hash Table)等,及其變種和組合。通常不必自己另外編寫,可以直接使用C++標(biāo)準(zhǔn)庫,例如STL。
術(shù)語“引用計(jì)數(shù)”及“引用數(shù)”,在本文中一般是指某對(duì)象的所有的有效引用的個(gè)數(shù),包括鎖定計(jì)數(shù)器中的值,有的時(shí)候也特指位于對(duì)象附加控制塊中的引用計(jì)數(shù)器的值。當(dāng)聯(lián)系到引用計(jì)數(shù)的值降到0時(shí)進(jìn)行的處理,總是指包括鎖定計(jì)數(shù)器和引用計(jì)數(shù)器在內(nèi)的所有引用數(shù)。
下面將結(jié)合附圖詳細(xì)描述本發(fā)明的優(yōu)選實(shí)施例一。
如圖1中的圖解,一個(gè)計(jì)算機(jī)系統(tǒng)可能是個(gè)人電腦(PC)、個(gè)人數(shù)字助手(PDA)、互聯(lián)網(wǎng)裝置、無線移動(dòng)電話、服務(wù)器、或者其他任何計(jì)算裝置。作為一個(gè)示范的例子,該計(jì)算機(jī)系統(tǒng)100包括一個(gè)主處理器單元101及其電源供給單元102。主處理器單元101包括一個(gè)或多個(gè)處理器103,并通過系統(tǒng)電路104連接到一個(gè)或多個(gè)內(nèi)存單元105,一個(gè)或多個(gè)接口設(shè)備106通過系統(tǒng)電路104連接到處理器103。在本示范例子中,系統(tǒng)電路104是一個(gè)地址/數(shù)據(jù)總線。當(dāng)然,一個(gè)所屬技術(shù)領(lǐng)域的技術(shù)人員可以使用其他非總線的方式連接處理器103和內(nèi)存單元105,例如使用一條或多條的專用線路,和(或者)縱橫交換器,將處理器103和內(nèi)存設(shè)備105連接起來。
處理器103可以包括各種微處理器,例如x86系列的Intel PentiumTM微處理器,AMD AthlonTM64微處理器等,或者PowerPC,ARM,MIPS等其他系列。內(nèi)存單元105包括隨機(jī)存儲(chǔ)內(nèi)存,例如DRAM(DynamicRandom Access Memory)等。在本例子中內(nèi)存單元存放著被處理器103執(zhí)行處理的代碼和數(shù)據(jù)。接口電路106可以使用各種接口標(biāo)準(zhǔn),例如USB、PCI、PCMCIA等。一個(gè)或多個(gè)輸入設(shè)備107包括鍵盤、鼠標(biāo)、觸摸屏、語音識(shí)別裝置等通過一個(gè)或多個(gè)接口電路106連接到主處理單元101。一個(gè)或多個(gè)輸出設(shè)備108包括顯示器、打印機(jī)、揚(yáng)聲器等通過一個(gè)或多個(gè)接口電路106連接到主處理單元101。系統(tǒng)還可以包括一個(gè)或多個(gè)外部存儲(chǔ)單元109,包括硬盤、CD/DVD等。系統(tǒng)通過網(wǎng)絡(luò)設(shè)備110與其他外部的計(jì)算機(jī)設(shè)備交換數(shù)據(jù),網(wǎng)絡(luò)設(shè)備包括以太網(wǎng)、DSL、電話線、無線網(wǎng)絡(luò)設(shè)備等。
圖2是一個(gè)本發(fā)明的實(shí)施例的結(jié)構(gòu)框圖,垃圾回收器200運(yùn)行在圖1所示的公知計(jì)算機(jī)上,由處理器103執(zhí)行位于內(nèi)存設(shè)備105中的代碼和數(shù)據(jù)。垃圾回收器200可以作為用戶模式(User-Mode)下的應(yīng)用程序的一部分,也可以是操作系統(tǒng)內(nèi)核的一部分,或者介于兩者之間的平臺(tái)組成部分,例如Java或.NET平臺(tái)的虛擬機(jī)的組成部分,也可以是其他需要內(nèi)存管理的系統(tǒng)的一部分。
在該圖中,垃圾回收器由以下部分組成GC接口模塊201、運(yùn)行態(tài)的引用計(jì)數(shù)模塊202、引用跟蹤回收器203、GC同步管理器204、線程管理器205、和GC堆管理器206。而GC編譯態(tài)輔助代碼212則是分散在應(yīng)用程序210的整個(gè)代碼中,目的是獲得最大限度的性能提升。應(yīng)用程序210可以直接地、無限制地訪問本地的堆和靜態(tài)的數(shù)據(jù)段209。精心設(shè)計(jì)的后端接口則提供了平臺(tái)無關(guān)的特性,令垃圾回收器200可以方便地移植到各種不同類型的平臺(tái)上,只要這些平臺(tái)支持虛擬內(nèi)存服務(wù)208和一些其他系統(tǒng)服務(wù)207。系統(tǒng)要求的平臺(tái)相關(guān)的服務(wù)211是如此的精簡(jiǎn),適合大多數(shù)的通用計(jì)算環(huán)境。
GC接口模塊201接受各種來自應(yīng)用程序的GC函數(shù)調(diào)用,并調(diào)用其他相關(guān)內(nèi)部模塊的服務(wù),完成應(yīng)用程序提出的服務(wù)請(qǐng)求。
運(yùn)行態(tài)引用計(jì)數(shù)模塊202提供引用計(jì)數(shù)的服務(wù),特別是回收引用計(jì)數(shù)為0的對(duì)象。當(dāng)一個(gè)對(duì)象的引用計(jì)數(shù)變成0,該模塊就在GC同步管理204的協(xié)調(diào)下回收該對(duì)象。
GC同步管理204是一個(gè)虛擬模塊,它的代碼分散在垃圾回收器200的各個(gè)內(nèi)部模塊中,這樣的設(shè)計(jì)提高了關(guān)鍵代碼的執(zhí)行效率。
引用跟蹤回收器203是整個(gè)系統(tǒng)中最重要的部分,它可以由應(yīng)用程序顯式地激發(fā),也可以有系統(tǒng)線程周期性地激發(fā)或者在其他條件下激發(fā)。它掃描所有受管理的對(duì)象,找出活動(dòng)的對(duì)象,跟蹤遍歷對(duì)象的引用關(guān)系圖,收集和釋放不可達(dá)的對(duì)象。
GC堆管理器206從虛擬內(nèi)存系統(tǒng)服務(wù)208中分配頁面,分割成小塊的內(nèi)存塊并簿記這些內(nèi)存塊的使用情況,并同時(shí)提供寫屏障(Write Barrier)和內(nèi)部指針(Interior Pointer)的定位服務(wù)。
線程管理器205為其他內(nèi)部模塊提供平臺(tái)無關(guān)的線程服務(wù),將各個(gè)不同平臺(tái)的線程服務(wù)統(tǒng)一到一致的函數(shù)調(diào)用,對(duì)于本身不支持線程的平臺(tái),則使用內(nèi)建的線程庫實(shí)現(xiàn)用戶線程,這樣對(duì)于其他模塊線程的實(shí)現(xiàn)方式就是透明的、無須理會(huì)具體的實(shí)現(xiàn)。應(yīng)用程序的線程可以通過該模塊登記成“非GC”的線程,以免受垃圾回收的打擾。該模塊提供線程暫停執(zhí)行和線程恢復(fù)執(zhí)行的調(diào)用,供引用跟蹤回收器使用。
本地堆和靜態(tài)數(shù)據(jù)段209是常規(guī)的malloc/free或者new/delete方式的堆,及靜態(tài)的數(shù)據(jù)段,包括全局變量、本地變量、類變量等。任何類型的傳統(tǒng)數(shù)據(jù)結(jié)構(gòu)都可以自由地存儲(chǔ)在這些區(qū)域,和傳統(tǒng)的C/C++程序一樣。然而,如果這些數(shù)據(jù)結(jié)構(gòu)中存在引用GC堆中的受管理的對(duì)象,而且希望通過這些引用保持GC對(duì)象的存活,則需要在適當(dāng)?shù)臅r(shí)候調(diào)用GC接口函數(shù)以通知系統(tǒng)相關(guān)的引用變化,后文有詳細(xì)說明。另外,如果應(yīng)用程序需要GC系統(tǒng)跟蹤到這些本地對(duì)象內(nèi)部,則一些同步的GC接口函數(shù)必須適當(dāng)?shù)乇徽{(diào)用。
接下來,為了更好地理解反映本發(fā)明的內(nèi)在精神,將詳細(xì)地分析和說明本發(fā)明的實(shí)質(zhì)和實(shí)施例。
精確的引用跟蹤回收本發(fā)明的一個(gè)實(shí)施例的目標(biāo)是提供精確(Accurate,Exactness or Precise)的垃圾引用跟蹤回收。精確的垃圾回收系統(tǒng)能夠回收所有的垃圾而不會(huì)造成內(nèi)存泄露,它要求能夠確定所有的指向受管理對(duì)象的引用,包括來自GC堆內(nèi)部的其他受管理對(duì)象,和來自GC堆外部的指針變量,例如來自線程的運(yùn)行棧中的指針類型的自動(dòng)變量。精確的垃圾回收有以下的優(yōu)點(diǎn)o消除因?yàn)楸J鼗厥赵斐傻哪承?duì)象不能回收,引起內(nèi)存泄露;o允許編譯器進(jìn)行最大程度的應(yīng)用程序代碼優(yōu)化,而不會(huì)象保守回收機(jī)制那樣出現(xiàn)問題;o消除了對(duì)象句柄造成的額外的開銷;o允許大量的不同的引用跟蹤方法可以在此基礎(chǔ)上得到應(yīng)用。
一個(gè)標(biāo)準(zhǔn)的精確垃圾回收系統(tǒng),它的GC回收操作不能隨意地在任意的執(zhí)行點(diǎn)進(jìn)行,必須只有在所有的線程都允許GC操作的時(shí)候才能進(jìn)行。垃圾回收操作只能在所有線程都是GC-Safe狀態(tài)下才能進(jìn)行。每個(gè)線程只能位于GC-Safe狀態(tài)或者GC-Unsafe狀態(tài)。處于GC-Unsafe狀態(tài)的線程可以自由地使用堆中的對(duì)象,和隨意地進(jìn)行GC指針操作,例如指針運(yùn)算等,由于GC系統(tǒng)對(duì)于GC-Unsafe狀態(tài)下的線程,不能掌握所有的必須的引用關(guān)系信息,所以不能在此狀態(tài)下進(jìn)行GC引用跟蹤。處于GC-Safe狀態(tài)的線程則將所有它使用的、影響GC回收的引用關(guān)系信息披露了出來,所以GC回收操作可以進(jìn)行。
通常,為了避免因?yàn)榫幾g器的優(yōu)化處理,例如使用寄存器保存指針,導(dǎo)致的部分引用關(guān)系不能被GC系統(tǒng)檢測(cè)發(fā)現(xiàn),有以下幾種處理技巧線程劫持(Thread Hijacking)-編譯器修改線程的運(yùn)行棧,將函數(shù)的返回地址指向某個(gè)特別的函數(shù),當(dāng)函數(shù)結(jié)束返回時(shí),該特殊函數(shù)就會(huì)被執(zhí)行,并暫停該線程的執(zhí)行等待GC回收處理。
插入安全點(diǎn)(Inserting Safe Point)-編譯器在適當(dāng)?shù)牡胤郊尤雽?duì)GC系統(tǒng)的調(diào)用,例如在一些長(zhǎng)時(shí)間的循環(huán)過程中,GC系統(tǒng)則檢查此時(shí)是否需要暫停該線程并開始垃圾回收。通過此方法來保證線程能夠在一定的時(shí)間內(nèi)到達(dá)GC-Safe的狀態(tài)點(diǎn)。
分布表(Layout Table)-編譯器生成一些數(shù)據(jù)表格,描述函數(shù)過程中每個(gè)點(diǎn)的指針使用情況,這樣的函數(shù)就是完全可以中斷的。此方法類似于程序的調(diào)試信息的產(chǎn)生,因此會(huì)帶來較大的代碼臃腫問題。
還有一些其他的方法,所有大部分的這些方法都需要編譯器的支持,通過插入這樣那樣的代碼,或者插入各式各樣的描述數(shù)據(jù)以供特定的GC系統(tǒng)使用。然而,常規(guī)的C++語言并沒有提供這樣的支持,C++的標(biāo)準(zhǔn)也沒有這方面的規(guī)定,例如如何生成或者生成怎么樣的描述數(shù)據(jù),而且這些數(shù)據(jù)的格式通常都與特定的GC回收器相關(guān),沒有也很難有統(tǒng)一的標(biāo)準(zhǔn)。所以,在C++的環(huán)境下,保守的垃圾回收方法占據(jù)了絕對(duì)的統(tǒng)治地位,但是保守的垃圾回收有很多在前面講到的問題。
本發(fā)明提出一個(gè)創(chuàng)造性的方法,令常規(guī)的C++程序也可以使用精確的垃圾回收。整個(gè)過程中,無須進(jìn)行應(yīng)用程序的根集指針掃描,所以更談不上保守地猜測(cè)類似指針的數(shù)據(jù)。而且,所有的應(yīng)用程序代碼自動(dòng)變成GC-Safe類型,在任何時(shí)候都可以立即進(jìn)行垃圾回收操作,而無須等待所有的線程進(jìn)入GC-Safe狀態(tài)。
方法的核心及抽象之后的版本是應(yīng)用程序在運(yùn)行時(shí),動(dòng)態(tài)標(biāo)識(shí)出哪些對(duì)象是確定處于活動(dòng)狀態(tài)的,然后在引用跟蹤回收過程中以這些對(duì)象為起點(diǎn),跟蹤遍歷,確定不可達(dá)對(duì)象并予以回收。
如何標(biāo)識(shí)出處于活動(dòng)狀態(tài)的對(duì)象,是方法的關(guān)鍵。下面詳細(xì)說明采用的方法。
首先,定義“擴(kuò)展根集”為所有非GC堆的區(qū)域。GC堆是指受管理對(duì)象的集合,不一定需要實(shí)際的連續(xù)的一段內(nèi)存空間,后面描述的一個(gè)實(shí)施例就沒有實(shí)際存在的GC堆。受管理對(duì)象是指通過GC系統(tǒng)分配的對(duì)象,并由GC系統(tǒng)維護(hù)和釋放,例如用戶自定義的結(jié)構(gòu)可以通過gcnew或者類似操作,請(qǐng)求GC系統(tǒng)創(chuàng)建一個(gè)受管理的對(duì)象并返回它的相關(guān)地址。這樣,擴(kuò)展根集合就包括靜態(tài)的數(shù)據(jù)段、線程的執(zhí)行棧、處理器的寄存器、本地的堆、等。從程序員的角度,則包括全局變量、本地變量、靜態(tài)變量、棧內(nèi)的自動(dòng)變量、函數(shù)的參數(shù)及返回值、還有位于上述區(qū)域及本地堆中的對(duì)象的成員變量。換句話說,就是除了受保護(hù)對(duì)象的成員變量之外的其他變量。
一個(gè)更加廣泛的“擴(kuò)展根集”的定義為所有將被引用跟蹤所遍歷到的指針或者有效指針不屬于擴(kuò)展根集,而其他的指針則屬于擴(kuò)展根集。這個(gè)定義比較難理解,但是更加準(zhǔn)確,因?yàn)樗祟愃七@樣的情況,指針位于本地堆中,但是將通過某個(gè)屬主對(duì)象,GC系統(tǒng)將跟蹤該指針及其引用的對(duì)象。
所有來自擴(kuò)展根集的引用將導(dǎo)致被引用的對(duì)象標(biāo)志成活動(dòng)的對(duì)象。例如,一個(gè)來自函數(shù)內(nèi)部的、指針類型的自動(dòng)變量,將導(dǎo)致該變量所指的對(duì)象標(biāo)識(shí)成活動(dòng)的狀態(tài)。對(duì)于來自擴(kuò)展根集的引用,系統(tǒng)應(yīng)該進(jìn)行監(jiān)視,維護(hù)一致的狀態(tài)關(guān)系至少是保守的一致的關(guān)系,即如果存在來自擴(kuò)展根集的引用,則該對(duì)象需要被標(biāo)識(shí)出來,有別于其他對(duì)象;反之,如果已經(jīng)沒有了來自擴(kuò)展根集的引用,則該對(duì)象應(yīng)該被取消標(biāo)識(shí),回歸到其他的對(duì)象集合中去。保守的一致性是指,站在垃圾回收的角度,允許某些垃圾對(duì)象在本次回收操作中“漏”掉,(而在以后的回收操作中被收集,也可以不收集,只是過于保守了),那么,就可以在標(biāo)識(shí)和實(shí)際情況上允許有一定的時(shí)間上的延遲,以提高效率。例如,當(dāng)某對(duì)象已經(jīng)沒有來自根集的引用,該對(duì)象仍可以暫時(shí)在一定時(shí)間內(nèi)保持著標(biāo)識(shí)狀態(tài)。
最直接的標(biāo)識(shí)方法是使用引用計(jì)數(shù)方法,系統(tǒng)為每個(gè)受管理對(duì)象維護(hù)一個(gè)鎖定計(jì)數(shù)器,該計(jì)數(shù)器代表來自擴(kuò)展根集中的引用個(gè)數(shù),同時(shí)作為該對(duì)象是否被標(biāo)識(shí)的判斷條件。如果增加了一個(gè)從擴(kuò)展根集的引用,則該鎖定計(jì)數(shù)器的值加1;反之,如果有一個(gè)來自擴(kuò)展根集的引用超出了作用域(Scope),則鎖定計(jì)數(shù)器的值減1;鎖定計(jì)數(shù)器的值如果是正數(shù),則代表該對(duì)象處于標(biāo)識(shí)的活動(dòng)狀態(tài),如果為0則表示該對(duì)象屬于其他的類型。
引用計(jì)數(shù)的方法直接而且簡(jiǎn)潔,但并不是唯一選擇,而且引用計(jì)數(shù)仍可以使用其他的變種,例如延遲引用計(jì)數(shù)(Deferred Reference Counting)、權(quán)重引用計(jì)數(shù)(Weighted Reference Count)等。在有必要的情況下,可以使用其他的方法,例如每個(gè)受管理對(duì)象對(duì)應(yīng)一個(gè)結(jié)構(gòu),當(dāng)擴(kuò)展根集中有新的變量引用該對(duì)象時(shí),系統(tǒng)判斷該對(duì)象所對(duì)應(yīng)的結(jié)構(gòu)是否位于特定的集合中,該集合可以采用各種公知的管理機(jī)制,例如鏈表、數(shù)組、向量、聚集、集合、圖、哈希表和及其組合等,系統(tǒng)維護(hù)該結(jié)構(gòu)在集合中的唯一性,并以引用該對(duì)象的指針變量的地址作為依據(jù),確保來自擴(kuò)展根集的引用與該結(jié)構(gòu)是否位于集合中保持一致。此方法開銷比引用計(jì)數(shù)要大,僅作為參考,在本實(shí)施例中并沒有采用。
具體的使用引用計(jì)數(shù)的方法如下系統(tǒng)提供了一組以template模板編寫的智能指針,供用戶編寫應(yīng)用程序使用。每種指針都針對(duì)特定的應(yīng)用場(chǎng)景,包括使用該指針的目的,和使用該指針的變量的位置。也就是說,指向同一個(gè)對(duì)象的指針不再是C/C++的語法書中描述的那樣的一種僅和對(duì)象類型相關(guān)的指針,而是可能有多種不同類型的智能指針。這也是本發(fā)明的一個(gè)特點(diǎn)之一,在后面的段落中有詳細(xì)的說明。
這些智能指針當(dāng)中,有一個(gè)名為“CLockedPtr”的智能指針,使用該類型的指針變量將總是保持所指的對(duì)象活動(dòng),而不會(huì)被回收。CLockedPtr指針缺省初始化為NULL,如果CLockedPtr初始化為某個(gè)對(duì)象的引用,則該對(duì)象的鎖定計(jì)數(shù)器的值將加1;當(dāng)CLockedPtr類型的指針變量離開作用域,則它所指向的對(duì)象的鎖定計(jì)數(shù)器的值將減1;當(dāng)將一個(gè)新的引用賦值給該智能指針變量時(shí),將導(dǎo)致一個(gè)原位(In Place)析構(gòu)調(diào)用及一個(gè)以新引用為初始值的原位構(gòu)造調(diào)用,這將導(dǎo)致原來所引用的對(duì)象的鎖定計(jì)數(shù)值減1和新引用的對(duì)象的鎖定計(jì)數(shù)值加1。
整體來說,CLockedPtr智能指針在鎖定計(jì)數(shù)器上完成了自動(dòng)引用計(jì)數(shù)的功能。每個(gè)CLockedPtr變量就是一個(gè)該對(duì)象的鎖的擁有者,而鎖定計(jì)數(shù)器的值就是引用該變量的CLockedPtr指針的個(gè)數(shù)。
系統(tǒng)永遠(yuǎn)不會(huì)回收釋放一個(gè)有非零的鎖定計(jì)數(shù)值的對(duì)象,該對(duì)象總是作為活動(dòng)的并且不會(huì)作為垃圾回收,運(yùn)行態(tài)的引用計(jì)數(shù)模塊也同樣會(huì)嚴(yán)格遵循這個(gè)原則,鎖定計(jì)數(shù)器的值也作為引用計(jì)數(shù)的一部分,只有鎖定計(jì)數(shù)器為0的情況下,對(duì)象才有可能被回收。
圖3A-C給出一個(gè)示意圖說明在一個(gè)實(shí)施例中,一個(gè)受管理的對(duì)象的附加控制塊及引用關(guān)系的示意。
圖3A給出一個(gè)附加在用戶自定義的數(shù)據(jù)結(jié)構(gòu)之前的控制塊。在其中一個(gè)實(shí)施例中,每個(gè)動(dòng)態(tài)經(jīng)過GC系統(tǒng)創(chuàng)建的對(duì)象300都有一個(gè)附加的控制塊301,用戶自定義的數(shù)據(jù)塊302則尾隨其后。當(dāng)應(yīng)用程序請(qǐng)求創(chuàng)建一個(gè)受管理的對(duì)象時(shí),需要給出該自定義對(duì)象的大小及遍歷函數(shù)等有關(guān)信息,在GC編譯態(tài)輔助代碼中有相關(guān)的機(jī)制幫助程序員方便地給出所需的信息,隨后的段落中有詳細(xì)描述。系統(tǒng)在創(chuàng)建了上述這樣的對(duì)象結(jié)構(gòu)并初始化控制塊之后,返回地址303。應(yīng)用程序通過該地址303按傳統(tǒng)的C/C++的方法使用該對(duì)象,可以使用“Dirty”的原始的指針304訪問對(duì)象的內(nèi)部數(shù)據(jù)等等。附加的控制塊301中,保存了一個(gè)鎖定計(jì)數(shù)器和一個(gè)普通的引用計(jì)數(shù)器,還有一些其他信息,例如對(duì)象的大小和遍歷函數(shù)的入口地址等。
圖3B是一個(gè)示意圖,代表一個(gè)受管理的對(duì)象。一個(gè)圓圈代表附加的控制塊311,而方塊則代表用戶自定義的數(shù)據(jù)塊312。
圖3C是一個(gè)引用關(guān)系示意圖,在圖的中部有六個(gè)受管理對(duì)象編號(hào)從321到326,它們位于GC堆中。對(duì)象321通過指針327、329引用對(duì)象322和323,這些有效的引用使用實(shí)線標(biāo)識(shí),將會(huì)導(dǎo)致對(duì)象保持存活。對(duì)象321還有一個(gè)指向?qū)ο?24內(nèi)部的指針328,該指針一般不能有效地維持對(duì)象的存活,所以用虛線表示。
通常,指向?qū)ο髢?nèi)部的指針是不會(huì)被GC回收器跟蹤的,不過應(yīng)用程序可以在遍歷函數(shù)中,先將指向?qū)ο髢?nèi)部的指針轉(zhuǎn)換成指向?qū)ο箢^部的有效指針,然后再通知GC系統(tǒng),這樣系統(tǒng)就可以正確地保持該對(duì)象有效,并跟蹤其引用的其他對(duì)象。某些實(shí)施例的內(nèi)存分配器提供內(nèi)部指針的定位服務(wù),給定某個(gè)對(duì)象的內(nèi)部地址,系統(tǒng)能查出該地址所屬的對(duì)象。這種服務(wù)的實(shí)現(xiàn)方法有很多,有的通過保持一個(gè)頁面內(nèi)的對(duì)象擁有相同的大小,從而確定對(duì)象的開始地址;更多的方法使用卡片(Card)方式,進(jìn)行一定范圍內(nèi)的保守的簿記,然后在此范圍內(nèi)掃描確定歸屬的對(duì)象。這些方法開銷都比較大,目前沒有較高效的令人滿意的算法,所以應(yīng)該盡量不要使用這種通用的內(nèi)部指針定位方法,而應(yīng)該由應(yīng)用程序根據(jù)實(shí)際情況有針對(duì)性地處理,將內(nèi)部指針轉(zhuǎn)換成對(duì)象指針。例如使用COM的接口方式獲得真正對(duì)象的地址,或者使用CONTAINING_RECORD宏將內(nèi)部指針轉(zhuǎn)成確定類型的對(duì)象地址。不過,如果運(yùn)行環(huán)境不在乎效率,例如解釋環(huán)境或者腳本語言,也可以使用這些通用的轉(zhuǎn)換方法,犧牲效率以換取使用上的便利。
系統(tǒng)允許使用原始的臟(Dirty)指針和指針運(yùn)算,這樣既兼容了原來的C++代碼又獲得了相當(dāng)高的執(zhí)行效率。程序員需要自己保證正確地使用原始的臟指針,使用的原則很簡(jiǎn)單,就是保證存在有效的對(duì)象引用的情況下才能使用臟指針,一旦失去了有效的對(duì)象引用,該對(duì)象就會(huì)立即被回收,所有的相關(guān)臟指針就絕對(duì)不能使用了。也就是說,要注意不要隨意失去對(duì)象的有效引用。舉例說明,在圖3C中,對(duì)象321因?yàn)榇嬖谟行б?27到對(duì)象322,如果應(yīng)用程序確定對(duì)象322引用了對(duì)象324,則應(yīng)用程序可以隨意地使用臟指針328訪問對(duì)象324;而指針330則是危險(xiǎn)的,因?yàn)闆]有有效的指針指向?qū)ο?25,對(duì)象325和326是循環(huán)引用的垃圾對(duì)象,它們將在下一次的垃圾回收操作中被回收釋放。
本系統(tǒng)有一個(gè)優(yōu)點(diǎn),就是不需要插入特殊的數(shù)據(jù)結(jié)構(gòu)到用戶自定義的對(duì)象中,在圖3A中,灰色的302部分完全由用戶定義。而目前的Java和.NET的對(duì)象模式都要求用戶自定義的類必須繼承于某個(gè)基本的全局通用的類,例如Object類。這個(gè)特性給予了程序員更大的自由,可以選擇某個(gè)特殊的基類去繼承,或者根本不繼承任何的其他的類。這樣就和C++的語義相符,大量的過去的投資得以保護(hù),例如,在本系統(tǒng)的支持下,微軟的COM可以不失兼容性的情況下,改進(jìn)支持垃圾回收機(jī)制。
引用跟蹤的垃圾回收可以在上述的方法下實(shí)施。為了便于讀者理解,下面先給出一個(gè)STW(Stop-The-World)類型的引用跟蹤回收器的實(shí)現(xiàn),之后再進(jìn)一步給出增量回收等方法。
圖4是一個(gè)“Stop-The-World”引用跟蹤垃圾回收操作的流程圖。首先,掛起所有的線程(步驟401);然后將所有的對(duì)象切換成“白”色(步驟402);掃描所有的對(duì)象,找出鎖定計(jì)數(shù)為正數(shù)的對(duì)象,標(biāo)記為“黑”色(步驟403);以這些鎖定的對(duì)象為起點(diǎn),跟蹤遍歷所有被引用的對(duì)象,將所有可達(dá)對(duì)象標(biāo)識(shí)成“黑”色(步驟404);最后恢復(fù)掛起的線程,然后回收那些“白”色的垃圾對(duì)象(步驟405)。
在整個(gè)過程中,不需要等待線程進(jìn)入GC-Safe狀態(tài),就可以直接掛起所有的線程;而且沒有掃描根集的操作,來自根集的引用已經(jīng)通過CLockedPtr智能指針將被引用的對(duì)象鎖定(鎖定計(jì)數(shù)不為零)。
由于引用計(jì)數(shù)的原子操作性,在任何時(shí)候,一個(gè)對(duì)象只能處于兩個(gè)狀態(tài),鎖定或者非鎖定。配合正確的鎖定和解鎖次序,對(duì)象總是保持正確的狀態(tài),沒有中間的過渡的不一致狀態(tài),GC系統(tǒng)總是獲得正確的、一致的引用關(guān)系圖。所以,全部的應(yīng)用程序代碼都支持GC,不存在GC-Unsafe狀態(tài),GC操作可以隨時(shí)開始。如果一個(gè)線程激發(fā)GC操作,系統(tǒng)可以立即開始掛起其他的線程,開始真正的工作,不需要象以前那樣等待應(yīng)用程序線程進(jìn)入GC-Safe安全點(diǎn)才能掛起線程,這是無暫停的增量垃圾回收的基礎(chǔ)之一。
從另一個(gè)角度來看,對(duì)象的引用只能存放在兩個(gè)地方,要么是在GC堆中,要么在GC堆之外。對(duì)于前者系統(tǒng)會(huì)按目前的技術(shù)進(jìn)行跟蹤遍歷,而對(duì)于后者,這些引用已經(jīng)在應(yīng)用程序的正常運(yùn)行的過程中引發(fā)了對(duì)象的狀態(tài)發(fā)生了變化,標(biāo)識(shí)出了這些受外部引用的對(duì)象。這樣,垃圾回收操作就只需要處理GC堆內(nèi)部的數(shù)據(jù)就可以了,引用關(guān)系圖從整個(gè)應(yīng)用程序轉(zhuǎn)化成GC堆內(nèi)部范圍。以GC堆的邊界劃分垃圾回收的工作區(qū)域,避開了外部復(fù)雜的情況,例如不需要考慮線程運(yùn)行棧內(nèi)的指針分布和處理器的寄存器使用情況。
在本實(shí)施例中,應(yīng)用程序僅僅是通過鎖定計(jì)數(shù)的方式標(biāo)識(shí)出被外界所引用的對(duì)象,由垃圾回收時(shí)再通過掃描確定鎖定的對(duì)象,作為遍歷的起點(diǎn)。另一種方法是,加大應(yīng)用程序執(zhí)行時(shí)的動(dòng)作,維護(hù)一個(gè)起始對(duì)象集合,將鎖定計(jì)數(shù)器的值為正數(shù)的對(duì)象加入到該集合中,在對(duì)象的鎖定計(jì)數(shù)器變?yōu)?時(shí),又將該對(duì)象從該集合中剔除;在引用跟蹤的垃圾回收的時(shí)候,系統(tǒng)直接使用該起始對(duì)象集合作為引用跟蹤的起點(diǎn)。這樣,可以減少垃圾回收時(shí)的開銷,無須掃描所有的對(duì)象。對(duì)于圖4的流程,只需要將步驟403去掉或者改為直接對(duì)該起始對(duì)象集合處理即可。
圖5A-C示意了一個(gè)對(duì)CLockedPtr智能指針的賦值操作,以便理解。
圖5A是初始化狀態(tài),CLockedPtr 501位于GC堆500之外,缺省初始化為空NULL;在GC堆500內(nèi),對(duì)象502通過指針504引用對(duì)象503,(假設(shè)對(duì)象502有其他指針引用它,從而保持其處于活動(dòng)狀態(tài))。因?yàn)閷?duì)象503僅由對(duì)象502所引用,其鎖定計(jì)數(shù)器的值為0而引用計(jì)數(shù)器的值為1。
圖5B示意在賦值發(fā)生之后,指向?qū)ο?03的引用504賦值到CLockedPtr 501智能指針上。由于原來CLockedPtr 501的值是空NULL,不需要將原來的對(duì)象的鎖定計(jì)數(shù)值減1;智能指針501現(xiàn)在有了一個(gè)指向?qū)ο?03的引用505,并且自動(dòng)將對(duì)象503的鎖定計(jì)數(shù)器的值加1;最終結(jié)果是對(duì)象503的鎖定計(jì)數(shù)器的值為1且引用計(jì)數(shù)器的值為1。
圖5C示意在賦值之后,原來的變量不再引用該對(duì)象了。指針504從圖中移除了,對(duì)象503的鎖定計(jì)數(shù)器的值仍為1而引用計(jì)數(shù)器的值為0,對(duì)象仍然有效。
對(duì)象鎖定計(jì)數(shù)器的維護(hù)確實(shí)帶來了一些運(yùn)行時(shí)的開銷,然而由于這些開銷通常發(fā)生在賦值操作的時(shí)候,而且絕大部分的操作是發(fā)生在線程棧中。對(duì)于一個(gè)以函數(shù)為主體的編程環(huán)境,這些開銷是可以消除的,下面將詳細(xì)說明如何去除這些開銷,并說明確定性的回收及析構(gòu)的方法。
確定性的回收機(jī)制本發(fā)明的一個(gè)實(shí)施例在上述的垃圾回收方法的基礎(chǔ)上提供了確定性的垃圾回收機(jī)制。
確定性的回收機(jī)制的主要優(yōu)點(diǎn)體現(xiàn)在資源的管理上。目前的垃圾回收方法不能保證對(duì)象能及時(shí)地釋放,所以需要程序員手工地顯式釋放對(duì)象所占據(jù)的其他非內(nèi)存的資源。通過確定性的回收機(jī)制,資源的管理變得清楚和簡(jiǎn)單,資源在對(duì)象的構(gòu)造過程中分配,在對(duì)象的析構(gòu)過程中釋放,即用對(duì)象來代表資源。系統(tǒng)在盡可能早的時(shí)期就將不可達(dá)的對(duì)象回收,釋放相關(guān)的資源。更確切地說,如果一個(gè)對(duì)象失去了所有的引用,則在最后一個(gè)引用失去的時(shí)候,系統(tǒng)會(huì)自動(dòng)回收該對(duì)象。
另一個(gè)優(yōu)點(diǎn)是確定性的回收不僅調(diào)用析構(gòu)函數(shù),而且還回收了對(duì)象所占用的內(nèi)存。因此,內(nèi)存得以盡可能早的回收,內(nèi)存的利用率大大提高了,引用跟蹤回收操作的周期可以大大延長(zhǎng),執(zhí)行效率得到提升。如果一個(gè)應(yīng)用程序得到精心的設(shè)計(jì),去除了循環(huán)引用的數(shù)據(jù)結(jié)構(gòu),則可以完全不需要執(zhí)行引用跟蹤回收操作,去除了該操作帶來的昂貴開銷。(當(dāng)然,有些人可能比較傾向一次過地回收所有的被垃圾占據(jù)的內(nèi)存,這只需要將確定性回收時(shí)釋放內(nèi)存塊的操作去除即可,只執(zhí)行對(duì)象析構(gòu)函數(shù),垃圾所占據(jù)的內(nèi)存由系統(tǒng)在引用跟蹤回收操作中一次過地回收。)從內(nèi)存利用率的角度,應(yīng)用程序的內(nèi)存使用量的變化在使用確定性回收之后,要比之前更加平滑,內(nèi)存使用量不再是持續(xù)地上升直到垃圾回收操作,而是盡早地釋放了,因此提供了更高的內(nèi)存使用率。
引用計(jì)數(shù)的方法可以提供一個(gè)很好的確定性的回收機(jī)制,本系統(tǒng)正是利用了這一點(diǎn),為每個(gè)對(duì)象維護(hù)一個(gè)鎖定計(jì)數(shù)器的同時(shí),還維護(hù)一個(gè)引用計(jì)數(shù)器。來自非擴(kuò)展根集的引用將導(dǎo)致引用計(jì)數(shù)器的值的變化。當(dāng)兩個(gè)計(jì)數(shù)器的值都到達(dá)零的時(shí)候,即意味著整個(gè)系統(tǒng)中沒有指向該對(duì)象的有效引用了,系統(tǒng)便將該對(duì)象回收。
問題是引用計(jì)數(shù)尤其是鎖定計(jì)數(shù)的維護(hù)開銷比較大,因?yàn)樗囊脕碜栽L問頻繁的根集區(qū)域,特別是在線程的執(zhí)行棧內(nèi)。如果采用延遲引用計(jì)數(shù)的方法,雖然效率提高了,但是它失去了確定性回收的性質(zhì),因此并不適用。本發(fā)明提出了一種新穎的提高引用計(jì)數(shù)器效率的方法,描述如下。
正如上文所述,在本發(fā)明的其中一個(gè)實(shí)施例中,引用計(jì)數(shù)整合在引用跟蹤回收的系統(tǒng)中,引用跟蹤利用引用計(jì)數(shù)來標(biāo)識(shí)出跟蹤遍歷的開始對(duì)象。系統(tǒng)提供一個(gè)頭文件(Header File),定義了有關(guān)的輔助用的宏(macro)、模板(template)和函數(shù)等。應(yīng)用程序的源代碼可以引用該頭文件,在編譯的時(shí)候?qū)⑦@些代碼嵌入(inline)到應(yīng)用程序的代碼中,通過使用編譯器的優(yōu)化功能,可以消除絕大部分的不必要的代碼執(zhí)行路徑。在運(yùn)行時(shí),引用計(jì)數(shù)模塊將系統(tǒng)內(nèi)部的數(shù)據(jù)與應(yīng)用程序分隔開,例如對(duì)象的附加控制塊的內(nèi)容對(duì)于應(yīng)用程序來說就是透明、不可見的。而引用計(jì)數(shù)模塊則直接使用引用跟蹤回收器的內(nèi)部數(shù)據(jù)及其他內(nèi)部數(shù)據(jù),在正確的同步管理機(jī)制下,協(xié)調(diào)來自不同線程的并發(fā)訪問。
提供高效的引用計(jì)數(shù)的關(guān)鍵在于以下的事實(shí),大部分的引用計(jì)數(shù)器的更新操作都發(fā)生在棧內(nèi)指針變量的操作中,而這些變量可以分成兩類,一類將導(dǎo)致被引用的對(duì)象的存活,而另一類則不能。例如圖6列出的偽代碼。
行號(hào)001-003,聲明了一個(gè)用戶自定義的類“CMyClass”,及兩個(gè)函數(shù)“funcB”和“funcC”;行號(hào)004-012,定義了函數(shù)“funcA”;行號(hào)013-018,定義了函數(shù)“funcC”,而函數(shù)“funcB”則是外部函數(shù),在其他模塊中定義;假設(shè)“funcA”被調(diào)用,參數(shù)是一個(gè)指向?qū)ο蟮囊?,在函?shù)“funcA”的執(zhí)行過程中,該對(duì)象保持有效。行008定義了一個(gè)自動(dòng)變量t2并在行009得到函數(shù)“funcB”的返回值。行010調(diào)用“funcC”,參數(shù)是兩個(gè)輸入性質(zhì)的對(duì)象引用,及一個(gè)變量地址以接收對(duì)象引用的返回值。
在此例子中,所有的輸入?yún)?shù)包括“pInObj”(行004),“pIn”(行013),“pIn2”(行013),它們都不需要保持所引用的對(duì)象存活。自動(dòng)變量則根據(jù)其用途,有些需要保持所引用的對(duì)象存活,有的則不需要。例如,在函數(shù)“funcA”中,變量“retval”和“t2”需要保持所引用的對(duì)象存活,而變量“t1”則不需要。正如前文所述,智能指針“CLockedPtr”將導(dǎo)致它所引用的對(duì)象存活,用CLockedPtr智能指針改寫圖6的偽代碼,改寫部分列在圖7中。
在圖7中,函數(shù)的原型作了一些修改,所有的從函數(shù)返回的指針類型均用CLockedPtr智能指針替換,例如行002,004,和013。自動(dòng)變量需要保持對(duì)象存活的也用智能指針進(jìn)行替換,如行006和008。注意這樣的事實(shí),行007中定義變量“t1”沒有變化。
經(jīng)過這樣的修改之后,產(chǎn)生的偽代碼已經(jīng)很接近最終的結(jié)果了。大部分的指針變量不需要使用智能指針進(jìn)行替換,訪問和賦值不會(huì)導(dǎo)致這些指針?biāo)玫膶?duì)象發(fā)生引用計(jì)數(shù)的變化,這些開銷被消除了。一般情況下,函數(shù)的輸入?yún)?shù)無需保持對(duì)象存活,而輸出的參數(shù)和返回值則通常需要保持對(duì)象存活,函數(shù)內(nèi)部的變量則視具體情況而定。
系統(tǒng)通過CLockedPtr智能指針進(jìn)一步地消除了引用計(jì)數(shù)的操作。到現(xiàn)在為止,引用計(jì)數(shù)的維護(hù)開銷來自兩個(gè)方面,一個(gè)是來自受管理對(duì)象的成員變量,另一個(gè)是來自CLockedPtr智能指針。一個(gè)新的智能指針“CMemberPtr”被引入,該智能指針用于替換受管理對(duì)象的成員指針變量。CMemberPtr智能指針只應(yīng)該出現(xiàn)在類的成員變量中,而且該類的實(shí)例應(yīng)該只作為GC系統(tǒng)創(chuàng)建的對(duì)象,屬于GC堆的一部分,或者本地堆的對(duì)象并附屬于某個(gè)GC對(duì)象,接受引用跟蹤。(作為靜態(tài)段或者線程棧的一部分被引用跟蹤也是可以的,不這種情況極其少見,而且應(yīng)該避免)CMemberPtr輔助CLockedPtr消除了引用計(jì)數(shù)的開銷,而且在無暫停的增量回收中與CLockedPtr一起發(fā)揮了關(guān)鍵的作用。當(dāng)一個(gè)引用賦值給CMemberPtr,系統(tǒng)自動(dòng)將原來引用的對(duì)象的引用計(jì)數(shù)器的值減1,將新引用的對(duì)象的計(jì)數(shù)值加1;指針離開作用域之后,原來引用的對(duì)象的計(jì)數(shù)值自動(dòng)減1;如果引用計(jì)數(shù)和鎖定計(jì)數(shù)都為0,則系統(tǒng)執(zhí)行該對(duì)象的析構(gòu)函數(shù),并可能導(dǎo)致更多的對(duì)象回收。
而作為CLockedPtr智能指針,它的實(shí)際功能還不只是前面所描述的保持對(duì)象存活和標(biāo)識(shí)遍歷的起始對(duì)象,系統(tǒng)根據(jù)不同的賦值操作的“右值”類型,使用不同的策略?!坝抑怠卑ㄙx值操作的參數(shù),和初始化過程的參數(shù)。
給定一個(gè)對(duì)象的引用,可以分成三種類型并屬于特定的區(qū)域1)原始指針或者臟指針,完全等同C/C++的指針,沒有任何智能的動(dòng)作,該類指針可以用于任何的區(qū)域;2)CMemberPtr智能指針,用于GC堆或相類似功能的區(qū)域;3)CLockedPtr智能指針,用于擴(kuò)展根集中。
當(dāng)將原始指針賦值給或初始化CMemberPtr或CLockedPtr智能指針時(shí),系統(tǒng)將自動(dòng)將原來引用的對(duì)象的引用計(jì)數(shù)器或鎖定計(jì)數(shù)器的值減1,將新引用的對(duì)象的相應(yīng)計(jì)數(shù)值加1。當(dāng)用原始指針或CMemberPtr賦值或初始化CLockedPtr時(shí),對(duì)象的鎖定計(jì)數(shù)值加1。
然而,當(dāng)CLockedPtr賦值或初始化操作是以另一個(gè)CLockedPtr指針作為參數(shù)時(shí),系統(tǒng)先將原來引用的對(duì)象的鎖定計(jì)數(shù)器的值減1,然后將新對(duì)象的引用“移動(dòng)”到該智能指針中,并不進(jìn)行鎖定計(jì)數(shù)器的更新操作,“右值”變量中的引用被清空。也就是說,CLockedPtr智能指針類型的變量之間的賦值操作更象一種引用的傳輸操作而不是一種拷貝引用的操作。
CLockedPtr額外提供一個(gè)名為“Duplicate”的成員函數(shù),它返回一個(gè)克隆的CLockedPtr引用。調(diào)用該函數(shù)將導(dǎo)致鎖定計(jì)數(shù)器的加1,而且原來的智能指針的值不發(fā)生變化。這樣,如果程序員需要原來那種導(dǎo)致引用數(shù)增加的賦值操作,他/她可以按以下偽代碼編寫程序p2=p1.Duplicate();其中p1,p2是CLockedPtr類型的智能指針變量。一般來說,在一個(gè)函數(shù)的代碼中,一個(gè)對(duì)象只需要一個(gè)CLockedPtr類型的智能指針就足夠了,過多的克隆的CLockedPtr指針只會(huì)增加運(yùn)行的開銷,因此應(yīng)該避免。事實(shí)上,需要多個(gè)CLockedPtr智能指針來引用一個(gè)對(duì)象,這種情況是非常少見的(如果真有這樣的必要)。
某些時(shí)候,C++的編譯器會(huì)自動(dòng)創(chuàng)建臨時(shí)的智能指針對(duì)象,由于CLockedPtr的拷貝構(gòu)造(CopyConstructor)函數(shù)和賦值操作符(Assignment Operator)函數(shù)都已經(jīng)重新定義了,缺省的操作已經(jīng)改成了移動(dòng)引用,不會(huì)產(chǎn)生鎖定計(jì)數(shù)的更新操作。所以,整個(gè)引用計(jì)數(shù)的開銷大大地降低了。
回到圖6的偽代碼和圖7的修改,最終的偽代碼如圖8所示。圖8中,存在三處顯式或隱藏的、在CLockedPtr智能指針之間的賦值操作,分別是行009,010,016和011。這些賦值操作都沒有引起鎖定計(jì)數(shù)器的維護(hù)操作,也就沒有相關(guān)的開銷。給一個(gè)極端的例子,假設(shè)函數(shù)A1調(diào)用函數(shù)A2,A2調(diào)用A3,如此以往,最后函數(shù)A100調(diào)用庫函數(shù)CreateObject創(chuàng)建一個(gè)對(duì)象,然后逐級(jí)將該對(duì)象的引用返回給調(diào)用者,最后回到函數(shù)A1,這整個(gè)過程不會(huì)產(chǎn)生引用計(jì)數(shù)器的維護(hù)開銷。
圖9A-B是一個(gè)示意圖,說明一個(gè)對(duì)象的引用是怎樣從函數(shù)中返回的。圖9A,在線程的運(yùn)行棧901中,智能指針902擁有對(duì)象904的鎖定引用;指針指針903位于調(diào)用者的棧結(jié)構(gòu)(Stack Frame)中,可以是圖8中的016行,也可以是圖8中的009行所示。在后一個(gè)例子中,事實(shí)上有兩次的賦值操作,一個(gè)是構(gòu)建一個(gè)臨時(shí)智能指針對(duì)象,隨后再將該臨時(shí)對(duì)象賦值給變量“t2”。圖9B是賦值之后的結(jié)果,對(duì)象的引用從智能指針902移到了智能指針903上,而對(duì)象的鎖定計(jì)數(shù)器的值并沒有發(fā)生變化,也不需要引用計(jì)數(shù)器的更新操作。
智能指針CLockedPtr和CMemberPtr可以輸出原始指針類型的引用,該操作不會(huì)導(dǎo)致引用計(jì)數(shù)器的更新操作,沒有產(chǎn)生引用計(jì)數(shù)的維護(hù)開銷。
至此,減少引用計(jì)數(shù)的開銷的方法已經(jīng)全部說明清楚了,該方法不僅可以象本實(shí)施例中這樣和引用跟蹤的標(biāo)識(shí)操作合并在一起(通過CLockedPtr智能指針),還可以獨(dú)立應(yīng)用于其他的環(huán)境中,只要是基于函數(shù)類型的編程模式下的使用引用計(jì)數(shù),通過該方法就能收到較好的效果。對(duì)于CMemberPtr智能指針可以進(jìn)一步的優(yōu)化,減少引用計(jì)數(shù)的開銷,例如使用類似CLockedPtr的移動(dòng)引用的操作,在移動(dòng)的過程中注意完成Write Barrier的簿記和注意內(nèi)存訪問次序的維護(hù)即可。又例如可以使用1bit的引用計(jì)數(shù)器,減少受管理對(duì)象之間的引用關(guān)系的維護(hù)操作的開銷,受管理對(duì)象一旦引用其他對(duì)象,則導(dǎo)致被引用對(duì)象的計(jì)數(shù)器(1-bit)置位,該置位操作不需要原子操作(Atomic),因此在多處理器的環(huán)境下可以提高并發(fā)的效率。系統(tǒng)還可以根據(jù)不同的情況和功能提供多個(gè)智能指針,方便使用并提供更加細(xì)致的管理信息。例如可以分別提供針對(duì)全局指針變量的智能指針,以區(qū)分線程棧內(nèi)CLockedPtr;或者同時(shí)提供總是進(jìn)行鎖定計(jì)數(shù)器維護(hù)的智能指針。各種方法都可以應(yīng)用,只要遵循標(biāo)識(shí)鎖定對(duì)象的這個(gè)精神,正確維護(hù)好鎖定計(jì)數(shù)器就可以了。
下面將要描述如何執(zhí)行對(duì)象的析構(gòu)函數(shù)。
一般來說,目前常規(guī)的引用計(jì)數(shù)機(jī)制是在應(yīng)用程序中維護(hù)引用計(jì)數(shù),當(dāng)引用數(shù)為0時(shí),直接執(zhí)行析構(gòu)函數(shù)并刪除對(duì)象。析構(gòu)函數(shù)執(zhí)行時(shí)是帶類型參數(shù)的,也就是說,調(diào)用析構(gòu)函數(shù)的代碼是確定對(duì)象的類型的。但是本系統(tǒng)中使用了引用跟蹤機(jī)制,這種方法首先便無法實(shí)現(xiàn),為了使跟蹤回收能夠正確地執(zhí)行適當(dāng)?shù)奈鰳?gòu)函數(shù),必須動(dòng)態(tài)地提供對(duì)象的信息包括析構(gòu)函數(shù)的入口地址。
本系統(tǒng)通過在每個(gè)對(duì)象的附加控制塊保存一個(gè)指向VClassInfo的接口的指針,完成動(dòng)態(tài)對(duì)象信息的提供。VClassInfo是一個(gè)類似COM的接口,通過它系統(tǒng)可以獲得所需的有關(guān)該對(duì)象的類的信息,并且方便以后的擴(kuò)充,將來的其他接口也可以從此接口中獲得。VClassInfo接口可以在本系統(tǒng)提供的輔助代碼的幫助下,由應(yīng)用程序的類自動(dòng)生成,應(yīng)用程序需要提供一個(gè)Traverse遍歷函數(shù)用于引用跟蹤,而析構(gòu)函數(shù)的入口系統(tǒng)則能夠自動(dòng)確定,對(duì)用戶透明。事實(shí)上,只要能夠提供動(dòng)態(tài)的對(duì)象信息即可,上述的方法僅僅為其中的一個(gè)例子,例如如果每個(gè)對(duì)象都能夠保證提供一個(gè)COM類似的接口,則對(duì)象的附加控制塊中就可以不必另外保存VClassInfo指針,可以直接請(qǐng)求對(duì)象的接口提供有關(guān)的對(duì)象信息,這樣就節(jié)省了一個(gè)指針大小的內(nèi)存空間。同樣使用語言本身的動(dòng)態(tài)信息機(jī)制也是可以的,例如C++語言的RTTI機(jī)制,使用的方法類似COM的接口,僅僅將接口改為C++的虛擬基類。
有了對(duì)象動(dòng)態(tài)信息的支持,給定一個(gè)對(duì)象的地址,系統(tǒng)可以獲得該對(duì)象的有關(guān)信息,包括對(duì)象的析構(gòu)函數(shù)入口,遍歷函數(shù)(Traverse)的入口。這樣,對(duì)象的析構(gòu)函數(shù)就不再由應(yīng)用程序自己調(diào)用了,而是由系統(tǒng)通過動(dòng)態(tài)信息機(jī)制進(jìn)行調(diào)用,例如通過引用跟蹤回收器和運(yùn)行態(tài)引用計(jì)數(shù)模塊完成。在一個(gè)多線程的環(huán)境下的實(shí)施例,析構(gòu)函數(shù)可能被其他線程異步地執(zhí)行,例如某個(gè)線程執(zhí)行引用跟蹤回收操作將該對(duì)象回收。
對(duì)于循環(huán)引用的垃圾對(duì)象,由于總是存在有效的、指向該對(duì)象的引用,本實(shí)施例尚未能立即回收這些對(duì)象,本實(shí)施例只能做到在對(duì)象的最后一個(gè)引用消失時(shí),立即回收。通過引用跟蹤回收,這些循環(huán)引用的垃圾最終將被回收,但是由于是循環(huán)引用的,回收的次序無法確定。也就是說,應(yīng)用程序不應(yīng)該依賴析構(gòu)函數(shù)的執(zhí)行次序。也許將來有更好的方法指出析構(gòu)的次序,例如通過創(chuàng)建對(duì)象的時(shí)間為依據(jù),但是沒有邏輯上的依據(jù)支持這樣的回收次序;而依賴應(yīng)用程序提供析構(gòu)的次序似乎也不太可能,如果一個(gè)應(yīng)用程序不能避免循環(huán)引用的數(shù)據(jù)結(jié)構(gòu),卻能夠提供這些垃圾對(duì)象的析構(gòu)次序,這種可能性很低。
另外,在傳統(tǒng)的C++編程習(xí)慣中,析構(gòu)函數(shù)通常刪除該對(duì)象所使用的子對(duì)象。在本系統(tǒng)中,一個(gè)對(duì)象可以擁有受管理的子對(duì)象和不受管理的、本地的子對(duì)象,直接刪除后者是可以的,但不應(yīng)該直接刪除受管理的對(duì)象,而是應(yīng)該清除指向該子對(duì)象的引用,由系統(tǒng)來完成子對(duì)象的回收。當(dāng)一個(gè)析構(gòu)函數(shù)執(zhí)行的時(shí)候,它可以做的是清除指向受管理對(duì)象的引用,和釋放其他本地的資源(native resource),包括本地堆中分配的對(duì)象。這樣做的原因是受管理的子對(duì)象可能被其他對(duì)象所引用,所以不能直接刪除;也因?yàn)樵搶?duì)象及其子對(duì)象可能被循環(huán)引用的對(duì)象所使用,成為循環(huán)引用的垃圾的一部分,而循環(huán)引用的垃圾對(duì)象的析構(gòu)函數(shù)的執(zhí)行是不確定次序的,子對(duì)象在析構(gòu)時(shí)有可能已經(jīng)提前被回收了。所以,在析構(gòu)函數(shù)中不能認(rèn)為子對(duì)象是存在的,不能隨意地使用子對(duì)象的成員變量和成員函數(shù),除非應(yīng)用程序能夠在其他邏輯結(jié)構(gòu)上保證子對(duì)象是有效的,例如有全局的有效的智能指針指向該子對(duì)象。
如果應(yīng)用程序確實(shí)需要一個(gè)嚴(yán)格的析構(gòu)函數(shù)執(zhí)行次序,可以這樣顯式地清除某些引用,令某些對(duì)象先變成垃圾對(duì)象,然后保持其他對(duì)象有效的情況下回收這些垃圾,應(yīng)用程序可以等待直到這些垃圾已經(jīng)回收了,然后再回收其他的對(duì)象。通過將要回收的對(duì)象分成幾組,分批進(jìn)行回收達(dá)到有次序地執(zhí)行析構(gòu)函數(shù)。本實(shí)施例提供了系統(tǒng)服務(wù)調(diào)用,能夠保證當(dāng)調(diào)用完成的時(shí)候,之前的垃圾已經(jīng)被回收。該系統(tǒng)服務(wù)實(shí)施的方法是1)首先判斷當(dāng)前是否有一個(gè)垃圾回收操作正在進(jìn)行,有則等待其結(jié)束;2)此時(shí)所有的引用數(shù)為0的對(duì)象已經(jīng)回收了,剩下的是循環(huán)引用的垃圾對(duì)象;3)啟動(dòng)一個(gè)新的垃圾回收操作,將之前的垃圾全部回收。注意,在垃圾回收操作過程中變成循環(huán)引用的垃圾對(duì)象,有可能不被當(dāng)前回收操作所回收,這是引用增量跟蹤垃圾回收方法的保守特性所決定的,所以上述步驟需要啟動(dòng)第二個(gè)垃圾回收操作。
對(duì)于已經(jīng)檢測(cè)出來的、循環(huán)引用的垃圾對(duì)象,系統(tǒng)可以這樣進(jìn)行析構(gòu)函數(shù)的調(diào)用。1)首先進(jìn)行第一遍處理,執(zhí)行每個(gè)對(duì)象的析構(gòu)函數(shù),在執(zhí)行之前先保證該對(duì)象的引用數(shù)不會(huì)下降到零,例如引用計(jì)數(shù)先加1;2)析構(gòu)函數(shù)的執(zhí)行過程中可能造成其他的對(duì)象的引用數(shù)降到零,可以直接回收這些零引用的對(duì)象(執(zhí)行析構(gòu)函數(shù)并釋放內(nèi)存)并從垃圾列表中剔除;3)進(jìn)行第二遍處理,將這些仍位于垃圾列表中的對(duì)象的內(nèi)存進(jìn)行回收。
同步機(jī)制與增量垃圾回收本實(shí)施例提供了一個(gè)增量的引用跟蹤回收器,它基于更新寫屏障(Update Write Barrier)方法。GC堆管理器通過操作系統(tǒng)的服務(wù)提供寫屏障的功能。所有的在跟蹤遍歷過程中發(fā)生修改的對(duì)象,都將被檢測(cè)出來,這些對(duì)象中的“黑”色對(duì)象則被改回“灰”色,并重新跟蹤遍歷。該方法存在一定的線程暫停要求,但是比起“Stop-The-World”方法暫停的時(shí)間要小得多,暫停時(shí)間比目前的其他一般增量垃圾回收方法也要小。
基本的增量垃圾回收過程可以描述為遍歷對(duì)象的引用關(guān)系圖,并標(biāo)記不同的顏色。對(duì)象邏輯上首先標(biāo)記成“白”色并等待回收,到遍歷的結(jié)束時(shí),該保留的對(duì)象將標(biāo)記為“黑”色,如果沒有可達(dá)的對(duì)象等待標(biāo)記為“黑”則遍歷結(jié)束。“灰”色意味著該對(duì)象是活動(dòng)的但是它所引用的對(duì)象尚未標(biāo)記和遍歷。
與此同時(shí),應(yīng)用程序正在并發(fā)地執(zhí)行著,某些對(duì)象的引用數(shù)可能下降到0。第一種處理方法是簡(jiǎn)單地跳過回收操作,等待引用跟蹤回收器來完成這些對(duì)象的回收。雖然,這些對(duì)象將最終被引用跟蹤回收器回收,但是由于幾乎所有(如果不是全部)的增量垃圾回收器都是保守的垃圾回收方法,也就是說,某些在回收過程中變成垃圾的對(duì)象可能不能被回收,而被當(dāng)作活動(dòng)的對(duì)象保留下來了。(不過,在回收過程開始之前就是垃圾的對(duì)象,則總是可以在這次回收過程中正確回收的)。因此,如果某對(duì)象的引用數(shù)降到0,跟蹤回收過程可能保守地將該對(duì)象當(dāng)作活動(dòng)對(duì)象,而只能在下一次的垃圾回收操作中才被回收。因?yàn)閮纱卫厥詹僮鞯拈g隔期可能很長(zhǎng),所以這些對(duì)象有可能被保留一段相當(dāng)長(zhǎng)的時(shí)間,這種處理方法不建議使用。
方法二,它保證了在垃圾回收結(jié)束的時(shí)候,其間引用數(shù)變?yōu)?的對(duì)象保證得到回收。應(yīng)用程序可以調(diào)用一個(gè)系統(tǒng)函數(shù),如果有一個(gè)垃圾回收過程在執(zhí)行,則該函數(shù)將等待該回收過程完成才返回;如果沒有并發(fā)的垃圾回收過程,則立即返回。這樣,應(yīng)用程序就可以保證在調(diào)用該函數(shù)之后,所有在此之前引用數(shù)就為0的對(duì)象已經(jīng)得到回收,析構(gòu)函數(shù)得到執(zhí)行。析構(gòu)函數(shù)的執(zhí)行次序是不確定的。
可以這樣實(shí)現(xiàn),維護(hù)一個(gè)零引用對(duì)象的列表,(也可以使用其他“常規(guī)集合技術(shù)”),當(dāng)垃圾回收操作過程中,某個(gè)對(duì)象的引用數(shù)降為0,則將該對(duì)象記錄在該列表中,跳過對(duì)象的回收操作,繼續(xù)應(yīng)用程序的下一步工作。當(dāng)跟蹤遍歷結(jié)束之后,這些記錄在列表中的零引用對(duì)象將被回收和析構(gòu)。由于該列表被多個(gè)線程共享訪問,適當(dāng)?shù)耐綑C(jī)制必須采用,以保證列表的數(shù)據(jù)完整。
方法三,可以在垃圾回收的過程中直接進(jìn)行零引用對(duì)象的回收。這種方法在實(shí)施例2中有詳細(xì)說明。
前面提到每個(gè)對(duì)象在提供動(dòng)態(tài)對(duì)象信息的時(shí)候,需要提供一個(gè)遍歷(Traverse)函數(shù),系統(tǒng)會(huì)在垃圾回收的過程中由回調(diào)該函數(shù),它的作用是描述該對(duì)象引用其他對(duì)象的情況,具體地說,就是指出該對(duì)象在當(dāng)前時(shí)刻引用哪些對(duì)象。實(shí)際操作中,程序員并不需要直接編寫該函數(shù),而是在一些宏定義的輔助下完成遍歷函數(shù)的定義,下面是一段示范的C++代碼class MyClass{...}; /*用戶自定義的類*/HNXGC_TRAVERSE(MyClass){ /*定義MyClass類的Traverse函數(shù)*/HNXGC_TRAVERSE_PTR(m_pNext);/*指出本對(duì)象引用m_pNext指出的對(duì)象*/… …/*其他被該對(duì)象引用的對(duì)象地址*/}可見,程序員只需要正確地使用本系統(tǒng)提供的宏即可方便地定義出遍歷函數(shù)。而且,HNXGC_TRAVERSE_PTR宏只要求一個(gè)被引用的對(duì)象的地址的值,即一個(gè)“右值”,并不需要是一個(gè)完整的指針,因此只要遍歷函數(shù)可以從對(duì)象的成員變量中,推算得出它所引用的對(duì)象就可以了。例如,類MyClass可以根據(jù)它的某個(gè)成員變量調(diào)用某些函數(shù),得到一個(gè)被引用的對(duì)象地址,然后以此地址作為HNXGC_TRAVERSE_PTR的參數(shù)即可。所以,只要有正確定義好的遍歷函數(shù)就足夠了,系統(tǒng)并不需要知道對(duì)象內(nèi)部的結(jié)構(gòu),對(duì)象可以有由簡(jiǎn)單到復(fù)雜的各種結(jié)構(gòu),包括位域(Bit Field)、聯(lián)合(Union)等過去屬于隱藏指針的,不能被一般的垃圾回收方法所處理的結(jié)構(gòu)。
本地的數(shù)據(jù)結(jié)構(gòu)也可以通過遍歷函數(shù)進(jìn)行描述,由系統(tǒng)自動(dòng)完成跟蹤。舉一個(gè)例子,假設(shè)一個(gè)受管理對(duì)象,它包含一個(gè)指向位于本地堆中的哈希表對(duì)象,該哈希表對(duì)象是純的C++的對(duì)象,它可能在本地堆中創(chuàng)建和管理多個(gè)其他子對(duì)象。哈希表可以加入對(duì)其他受管理對(duì)象的引用,每次增加這樣的引用的時(shí)候,應(yīng)用程序調(diào)用GC接口函數(shù),增加所引用對(duì)象的引用計(jì)數(shù);在哈希表中剔除引用時(shí),則減少相關(guān)對(duì)象的引用計(jì)數(shù);在遍歷函數(shù)中,則應(yīng)該調(diào)用該哈希表的成員函數(shù),枚舉所有引用的對(duì)象,然后再將這些對(duì)象通知GC系統(tǒng);析構(gòu)的時(shí)候,則先枚舉所有引用的對(duì)象,并減少這些對(duì)象的引用計(jì)數(shù),然后再刪除該哈希表對(duì)象。
要注意的是,遍歷函數(shù)是由其他線程異步地調(diào)用的,一般情況下是線程安全的,但是如果受管理對(duì)象采用了動(dòng)態(tài)的數(shù)據(jù)結(jié)構(gòu),而且遍歷函數(shù)的正確執(zhí)行必須依賴這些動(dòng)態(tài)結(jié)構(gòu),則需要嚴(yán)格地考慮多線程的競(jìng)爭(zhēng)問題。應(yīng)用程序在修改這些動(dòng)態(tài)結(jié)構(gòu)的時(shí)候,需要禁止該遍歷函數(shù)的執(zhí)行,這不能使用一般的同步機(jī)制,因?yàn)楸闅v函數(shù)執(zhí)行之前需要暫停其他線程的執(zhí)行,如果遍歷函數(shù)等待某個(gè)掛起的線程的同步資源,則會(huì)造成死鎖。為解決此問題,系統(tǒng)提供了一個(gè)專門的排它鎖及鎖定服務(wù),當(dāng)應(yīng)用程序需要修改某些關(guān)鍵的數(shù)據(jù)結(jié)構(gòu)時(shí),而且這些修改會(huì)造成不一致的數(shù)據(jù)關(guān)系,并影響遍歷函數(shù)的正確執(zhí)行,則可以通過調(diào)用該函數(shù),禁止遍歷函數(shù)在此期間執(zhí)行,同樣當(dāng)垃圾回收操作處于關(guān)鍵的階段,例如將要暫停所有線程作最后的遍歷的時(shí)候,將鎖定該鎖。因此,當(dāng)應(yīng)用程序成功獲得該鎖時(shí),垃圾回收過程一定不會(huì)執(zhí)行遍歷函數(shù),也不會(huì)執(zhí)行暫停線程等可能造成死鎖的操作,系統(tǒng)會(huì)等待直到應(yīng)用程序釋放該鎖。在上述的哈希表例子中,由于遍歷函數(shù)需要一致的哈希表狀態(tài),所以哈希表的成員函數(shù)的操作都必須使用這種鎖進(jìn)行保護(hù)。(除非應(yīng)用程序保證總是顯式地執(zhí)行垃圾回收操作,并且在執(zhí)行期間不會(huì)發(fā)生哈希表的訪問。)為了配合增量的垃圾回收,對(duì)于上述的跟蹤到本地對(duì)象中的情況,應(yīng)用程序還在引用關(guān)系發(fā)生變化的時(shí)候,調(diào)用一個(gè)專門的函數(shù),通知GC系統(tǒng)。該函數(shù)指出發(fā)生引用變化的對(duì)象,以便系統(tǒng)能夠采取相應(yīng)的動(dòng)作,維護(hù)跟蹤遍歷的正確性。該函數(shù)必須在增加引用之后,而原來的引用變量仍保持有效的情況下進(jìn)行。該函數(shù)的調(diào)用是寫屏障的變型,函數(shù)的實(shí)施與引用跟蹤的方法密切相關(guān),本實(shí)施例中,它將導(dǎo)致該發(fā)生修改的對(duì)象從“黑”變“灰”,等待重新遍歷。而在實(shí)施例2中,該函數(shù)為空函數(shù)。
多個(gè)并發(fā)的垃圾回收請(qǐng)求也應(yīng)該使用同步機(jī)制進(jìn)行保護(hù),本實(shí)施例中只允許一個(gè)垃圾回收操作在執(zhí)行;多處理器的環(huán)境下,可以采用更加細(xì)致的同步管理,允許多個(gè)線程并發(fā)地進(jìn)行垃圾回收。
圖10A是增量垃圾回收的流程圖,圖10B是圖10A中確定鎖定對(duì)象步驟的詳細(xì)流程圖。
步驟1001試圖鎖定一個(gè)全局排它鎖,以保證只有一個(gè)實(shí)例在運(yùn)行垃圾回收操作。如果已經(jīng)存在一個(gè)正在運(yùn)行的垃圾回收操作,則等待該回收操作完成,然后結(jié)束返回。
步驟1002將所有的對(duì)象切換成“白”色。通過改變標(biāo)志所代表的含義,該操作很快且為常數(shù)復(fù)雜度。
步驟1003、1004、1005暫停所有的線程(除了顯式聲明為非GC線程),然后調(diào)用GC堆管理器開始一個(gè)寫屏蔽監(jiān)視,然后恢復(fù)線程的執(zhí)行。從此之后,所有的對(duì)GC堆的寫操作都將記錄在下來。
步驟1006掃描所有的受管理的對(duì)象,找出那些鎖定的“白”色對(duì)象,將他們送入“灰”色集合等待遍歷。該操作的復(fù)雜度正比于受管理對(duì)象的個(gè)數(shù),所以,可以在掃描過程中,每完成一定工作,例如每次掃描了20個(gè)對(duì)象,就釋放一次控制權(quán),令應(yīng)用程序可以創(chuàng)建新的對(duì)象。本操作的目的是將鎖定的對(duì)象加到“灰”色的集合中,各種方法都可以完成該步驟,例如可以由應(yīng)用程序動(dòng)態(tài)地將鎖定的對(duì)象加到某個(gè)特定的起始對(duì)象集合中,而在此步驟中則直接將起始對(duì)象集合送入“灰”色對(duì)象集合。
步驟1007以“灰”色對(duì)象為起點(diǎn),跟蹤遍歷所有的對(duì)象。可以這樣實(shí)現(xiàn)從“灰”色對(duì)象集合中逐個(gè)取出對(duì)象,調(diào)用對(duì)象的遍歷函數(shù);如果該對(duì)象所引用的對(duì)象是“白”色,則將該“白”色對(duì)象加到“灰”色對(duì)象集合中,等待下一次遍歷;調(diào)用過遍歷函數(shù)的對(duì)象則送到“黑”色集合中;不斷地從“灰”色集合中取出下一個(gè)對(duì)象進(jìn)行處理,直到“灰”色集合為空。
如果整個(gè)遍歷過程中處理的對(duì)象的個(gè)數(shù)很少,滿足預(yù)設(shè)條件則跳到最后的處理階段(步驟1010),而無須進(jìn)行增量的跟蹤遍歷。
步驟1008開始增量的跟蹤遍歷,直到條件滿足進(jìn)到步驟1010進(jìn)行最后的處理。增量跟蹤遍歷的過程是回收器盡量掃描更多的“白”色對(duì)象到“灰”色,跟蹤遍歷令“灰”變成“黑”的過程;而應(yīng)用程序則不斷修改引用關(guān)系圖,捕獲的這些修改并導(dǎo)致“黑”色對(duì)象的重新變“灰”,重新跟蹤遍歷的過程。
步驟1008類似于步驟1006,區(qū)別在于掃描鎖定對(duì)象的過程中不再釋放控制權(quán),并保持對(duì)象集合的鎖定,因此在此過程中,不允許有新的對(duì)象創(chuàng)建。
步驟1009指令GC堆管理器報(bào)告從步驟1004或上一次執(zhí)行步驟1009,以來發(fā)生過修改的對(duì)象。該步驟是在暫停所有線程的情況下調(diào)用的,修改過的對(duì)象如果是“黑”色則需要重新加到“灰”色集合中,重新跟蹤遍歷。
步驟1010開始最后的處理階段,通常當(dāng)回收器的速度趕上應(yīng)用程序,或者系統(tǒng)發(fā)現(xiàn)回收器很難趕上應(yīng)用程序時(shí),將進(jìn)入最后的處理階段。此階段將暫停所有線程,并在暫停的狀況下完成跟蹤遍歷的操作,因此在處理過程中不會(huì)有并發(fā)的引用關(guān)系的修改。此步驟的開銷不是想象中的那么大,因?yàn)榻^大部分的對(duì)象已經(jīng)改成了“黑”色,跟蹤遍歷的范圍要小得多。
步驟1011回收所有的垃圾對(duì)象,并釋放排他鎖,結(jié)束退出。而其他阻塞在該鎖上的并發(fā)垃圾回收操作也將自動(dòng)被喚醒而返回。
在整個(gè)增量回收操作的過程中,有幾個(gè)地方是需要暫停應(yīng)用程序線程的,其中開銷最大的處理是在最后階段的跟蹤遍歷。下面將要描述另一種增量垃圾回收的方法,可以完全避免暫停應(yīng)用程序線程。
優(yōu)選實(shí)施例二在圖1所示的公知計(jì)算機(jī)內(nèi),具備處理器和內(nèi)存設(shè)備,可以運(yùn)行預(yù)先編制的程序,包括二進(jìn)制代碼、P-Code等中間代碼、及源代碼等。圖11是本發(fā)明的實(shí)施例的結(jié)構(gòu)框圖,垃圾回收器1103運(yùn)行在圖1所示的公知計(jì)算機(jī)上,作為內(nèi)存管理的系統(tǒng)的一部分。垃圾回收器1103包括以下部分GC接口模塊1104、運(yùn)行態(tài)的引用計(jì)數(shù)模塊1106、引用跟蹤回收器1105、和虛擬GC堆模塊1107。而GC編譯態(tài)輔助代碼1102則是分散在應(yīng)用程序1101的整個(gè)代碼中。系統(tǒng)直接從本地堆1108中分配受管理對(duì)象。
本實(shí)施例的絕大部分與實(shí)施例一相同,因此如果沒有特別的指出,可以認(rèn)為和實(shí)施例一是相同的。從結(jié)構(gòu)框圖可以發(fā)現(xiàn)主要的部分,例如引用計(jì)數(shù)模塊1106、引用跟蹤回收器1105,都依然存在。但是也會(huì)發(fā)現(xiàn),本實(shí)施例比實(shí)施例一要精簡(jiǎn)了若干組成部分,包括線程管理模塊205,GC堆管理器206等。這主要是因?yàn)楸緦?shí)施例使用新的寫屏障機(jī)制,從而不再需要特定的GC堆管理器來分配內(nèi)存,以提供寫屏障的服務(wù)。實(shí)施例一是通過GC堆管理器206,進(jìn)行受管理對(duì)象的分配和釋放,并在操作系統(tǒng)的幫助下提供寫屏障的服務(wù)的,它依賴于虛擬內(nèi)存機(jī)制和特定的內(nèi)存分配或簿記機(jī)制。而在本實(shí)施例中,系統(tǒng)是通過引用計(jì)數(shù)器的維護(hù)操作同時(shí)完成了寫屏障的功能,不需要特定的GC堆分配方法。事實(shí)上,本系統(tǒng)直接使用本地堆來創(chuàng)建受管理對(duì)象。虛擬GC堆模塊1107是一個(gè)邏輯上的模塊,它提供一些必要的管理功能,例如列出所有受管理的對(duì)象。
使用引用計(jì)數(shù)器的維護(hù)代碼來完成寫屏障的好處是避免掛起應(yīng)用程序線程。另一個(gè)好處是不再要求操作系統(tǒng)提供SuspendThread、GetWriteWatch這類的系統(tǒng)服務(wù),甚至連虛擬內(nèi)存的服務(wù)也可以不用提供。這樣,系統(tǒng)的適用范圍就更加廣泛了。再一個(gè)好處是這種寫屏障是指針修改操作時(shí)立即發(fā)生的,它比使用GetWriteWatch或者虛擬內(nèi)存的寫保護(hù)等機(jī)制要更加強(qiáng)大,它可以直接使用各種同步機(jī)制而不用擔(dān)心因?yàn)閽炱鹁€程而造成死鎖。
在一個(gè)指針的賦值操作中,涉及原來所引用的對(duì)象的計(jì)數(shù)值遞減,新引用的對(duì)象的計(jì)數(shù)值遞增,在引用計(jì)數(shù)遞減操作中可以實(shí)現(xiàn)Snapshot-at-beginning增量回收所需要的寫屏障,在引用計(jì)數(shù)遞增操作中可以實(shí)現(xiàn)Incremental update增量回收的向前變種的寫屏障。前者需要記錄下增量回收操作期間,被改變的指針的原來內(nèi)容(即原來引用的對(duì)象),后者則需要記錄下新引用的對(duì)象。對(duì)于Incremental update增量回收的滾回變種方法,則可以通過記錄下指針本身所屬于的對(duì)象而實(shí)現(xiàn)。(實(shí)施例一采用的就是Incremental update增量滾回的垃圾回收方法。)上述中的引用計(jì)數(shù)器顯然包括鎖定計(jì)數(shù)器和引用計(jì)數(shù)器,因?yàn)閬碜詫?duì)象成員變量的引用和來自擴(kuò)展根集的引用都同等重要。本實(shí)施例以Incremental update增量回收的向前變種作為基礎(chǔ),配合1)使用標(biāo)識(shí)鎖定的對(duì)象,避開了根集指針的掃描操作;2)所有代碼自動(dòng)GC-Safe;3)包括根集指針在內(nèi)的寫屏障支持;加以改進(jìn)之后,提供了一個(gè)完全沒有線程掛起操作的增量回收方法。下面是對(duì)改進(jìn)的詳細(xì)說明。
引用跟蹤回收器1105現(xiàn)在更加緊密地與引用計(jì)數(shù)模塊1106相關(guān)聯(lián),引用計(jì)數(shù)模塊1106可以直接訪問引用跟蹤回收器1105的內(nèi)部數(shù)據(jù)結(jié)構(gòu),當(dāng)然必要的同步機(jī)制是不可以少的?;镜脑瓌t沒有變化,仍然將指針分成三類,原始指針、CLockedPtr和CMemberPtr;鎖定的對(duì)象仍作為跟蹤遍歷的開始對(duì)象;二進(jìn)制的GC接口沒有變化,兼容實(shí)施例一的應(yīng)用程序二進(jìn)制代碼。垃圾回收操作分成兩個(gè)階段,標(biāo)記(Mark)階段和回收階段,在此過程中,應(yīng)用程序可以不間斷地修改引用關(guān)系圖,無須暫停。一旦垃圾對(duì)象被確定,就可以“從容”地回收這些垃圾對(duì)象,不必?fù)?dān)心與應(yīng)用程序發(fā)生競(jìng)爭(zhēng),垃圾是不會(huì)被應(yīng)用程序所訪問的。應(yīng)用程序?qū)σ藐P(guān)系圖的修改被引用計(jì)數(shù)模塊所捕獲,在本實(shí)施例中,只捕獲賦值操作的“右值”對(duì)象,具體地說,就是鎖定計(jì)數(shù)器和引用計(jì)數(shù)器的加1操作被捕獲,操作的目標(biāo)對(duì)象被記錄。一個(gè)專門的函數(shù)用于處理捕獲,稱為“賦值修改”(Assignment Mutator)。從回收器的角度,應(yīng)用程序異步地調(diào)用該函數(shù)。
圖12歸納了回收操作中的標(biāo)記階段的流程。
先有這樣的前提假設(shè),1)系統(tǒng)保證只有一個(gè)垃圾回收實(shí)例在運(yùn)行;2)全部的有效引用都應(yīng)該正確地調(diào)用系統(tǒng)的引用計(jì)數(shù)操作,正如智能指針?biāo)龅哪菢樱?)系統(tǒng)能夠提供受管理對(duì)象的列表;4)在此期間引用計(jì)數(shù)為0的對(duì)象暫不討論,后續(xù)有詳細(xì)的分析。
步驟1201,完成跟蹤遍歷的準(zhǔn)備工作,包括1)獲得訪問內(nèi)部數(shù)據(jù)結(jié)構(gòu)的保護(hù)鎖;2)設(shè)置內(nèi)部數(shù)據(jù)狀態(tài),例如切換所有對(duì)象成“白”色,設(shè)置標(biāo)志指出標(biāo)記工作的開始;3)釋放保護(hù)鎖。此步驟完成之后,所有對(duì)象變成“白”色,其中有一部分擁有正的鎖定計(jì)數(shù)值,而引用計(jì)數(shù)器的值則不予考慮。
步驟1202是最主要的,它的工作是將“白”色的鎖定的對(duì)象轉(zhuǎn)成“灰”色,然后調(diào)用“灰”色對(duì)象的遍歷函數(shù),將該對(duì)象所引用的“白”對(duì)象轉(zhuǎn)成“灰”色,完成遍歷函數(shù)的調(diào)用之后,將“灰”對(duì)象改為“黑”。在此期間,并發(fā)的應(yīng)用程序的賦值操作將被捕獲,而導(dǎo)致更多的“白”對(duì)象改為“灰”等待處理。本步驟必須不斷地處理“灰”對(duì)象,執(zhí)行其遍歷函數(shù),改為“黑”色,直到?jīng)]有“灰”色的對(duì)象。
步驟1203檢查是否存在“灰”色對(duì)象,如果該檢查涉及與應(yīng)用程序共享內(nèi)部數(shù)據(jù)結(jié)構(gòu),則需要鎖定保護(hù)。如果還有“灰”色對(duì)象則繼續(xù)步驟1202,沒有則繼續(xù)下一步驟。
步驟1204一旦程序執(zhí)行到本步驟,意味著沒有“灰”色對(duì)象了,可以推導(dǎo)得出此后不可能再出現(xiàn)“灰”色對(duì)象,所以,標(biāo)記工作到此結(jié)束,所有對(duì)象被分成兩組,“黑”的和“白”的。
新創(chuàng)建的對(duì)象在初始化之前不會(huì)引用任何其他對(duì)象,隨后初始化中的賦值操作將被系統(tǒng)捕獲,所以,在步驟1202期間新創(chuàng)建的對(duì)象不需要執(zhí)行遍歷函數(shù),可以直接標(biāo)記成“黑”或者“白”。本實(shí)施選擇標(biāo)記成“黑”,這樣可以保證系統(tǒng)中的“白”對(duì)象的數(shù)量不會(huì)增加。
對(duì)于賦值操作,系統(tǒng)不僅僅是按傳統(tǒng)的寫屏障將新引用的對(duì)象記錄下來,而且通過同步的機(jī)制與回收器進(jìn)行交互,形成多線程的處理并發(fā)處理,這樣就無須使用掛起線程的操作,而只要使用同步機(jī)制短時(shí)間的在競(jìng)爭(zhēng)時(shí)阻塞即可。整個(gè)增量回收過程便成為了一個(gè)回收線程與多個(gè)并發(fā)的賦值操作和創(chuàng)建新對(duì)象操作的多線程同步關(guān)系。
結(jié)合這兩種并發(fā)的操作,步驟1202可以描述成這樣垃圾回收系統(tǒng)掃描所有對(duì)象(新創(chuàng)建的對(duì)象除外),將“白”色的縮定對(duì)象改為“灰”色;掃描的次序和方法可以各種各樣,并且可以夾雜著其它的操作,例如調(diào)用對(duì)象的遍歷函數(shù),只要能夠全面地、完整地將步驟1201之前的所有的對(duì)象都掃描一遍就可以了。掃描過程中,可能有些原來鎖定的對(duì)象又變成的非鎖定的,由于賦值操作已經(jīng)被系統(tǒng)捕獲了,這種變化不會(huì)影響方法的正確性。某些實(shí)施例直接在運(yùn)行時(shí),就將鎖定的對(duì)象移到了一個(gè)專門的起始對(duì)象集合中了,則本步驟就無須再掃描全體對(duì)象來確定遍歷的起始對(duì)象了,可以直接使用該起始對(duì)象集合。
只要有“灰”的對(duì)象,系統(tǒng)就可以開始調(diào)用該對(duì)象的遍歷函數(shù),完成該對(duì)象的跟蹤遍歷操作,不一定要等所有的起始對(duì)象被確定。各種不同的遍歷方法都可以采用,它可以是深度遍歷、廣度遍歷、或者任意的窮盡(Exhaustive)的遍歷方法。當(dāng)一個(gè)對(duì)象的所有引用的對(duì)象都變成了“灰”色,則該對(duì)象就可以變成“黑”色。遍歷的方法必須是窮盡的,即當(dāng)沒有并發(fā)的引用關(guān)系圖的修改發(fā)生時(shí),遍歷必須正確地將“黑”、“白”對(duì)象區(qū)分出來,保證不會(huì)有活動(dòng)的對(duì)象沒有被遍歷到,而導(dǎo)致錯(cuò)誤標(biāo)記為“白”色的垃圾對(duì)象。
與此同時(shí),并發(fā)的引用關(guān)系圖修改將導(dǎo)致“白”色的對(duì)象轉(zhuǎn)成“灰”色,系統(tǒng)必須同樣處理這些“灰”色對(duì)象,執(zhí)行遍歷函數(shù),將它們轉(zhuǎn)成“黑”色。系統(tǒng)需要不斷地執(zhí)行步驟1202直到?jīng)]有“灰”色的對(duì)象為止。系統(tǒng)的遍歷工作總是能趕上應(yīng)用程序?qū)σ藐P(guān)系的修改的,這是本方法的特性所決定的。因?yàn)殡S著系統(tǒng)的遍歷工作的進(jìn)行,“灰”色的對(duì)象不斷地變成“黑”色,應(yīng)用程序則不斷將“白”色轉(zhuǎn)成“灰”色,而方法保證了“白”色對(duì)象的個(gè)數(shù)不會(huì)增加,所以總會(huì)有一個(gè)時(shí)刻,全部的“灰”色對(duì)象都轉(zhuǎn)成了“黑”色,系統(tǒng)的遍歷趕上了應(yīng)用程序?qū)σ藐P(guān)系的修改。
一旦“灰”色的對(duì)象都轉(zhuǎn)成了“黑”色,則不會(huì)再有新的“灰”色對(duì)象產(chǎn)生。原因如下,步驟1202保證了窮盡的遍歷和捕獲操作期間的對(duì)引用關(guān)系的修改。采用反證法,首先如果假設(shè)在步驟1203的時(shí)候,已經(jīng)沒有了“灰”色對(duì)象,但是存在一個(gè)隱藏的有效引用指向“白”色對(duì)象,而且該引用在步驟1203之前就已經(jīng)成功建立好了。那么有在圖12B中,對(duì)象分成兩類,“白”色和“黑”(及“灰”)色。其中,對(duì)象L 1211是循環(huán)引用的垃圾之一,對(duì)象H 1212和對(duì)象K 1213是“黑”色對(duì)象,一個(gè)未被檢測(cè)到的引用1214指向?qū)ο驦 1211。由于所有的有效指針都應(yīng)該用智能指針替換(或者按智能指針相同的方法處理),那么實(shí)施引用1214的智能指針有兩種可能,該指針的最后一次賦值操作發(fā)生在步驟1201之前,或者發(fā)生在其之后。
假設(shè)發(fā)生在步驟1201之后,由于該引用已經(jīng)在步驟1203之前就建立好了,所以該最后一次的賦值操作必然被系統(tǒng)捕獲,賦值操作的“右值”對(duì)象L 1211則會(huì)被檢查,而從“白”轉(zhuǎn)成了“灰”,此結(jié)果與假設(shè)發(fā)生矛盾。
如果最后一次賦值操作發(fā)生在步驟1201之前,則又有兩種可能,如果智能指針是CLockedPtr類型,則對(duì)象L 1211將一直保持鎖定(因?yàn)槭亲詈笠淮钨x值操作),所以系統(tǒng)能夠正確將對(duì)象L 1211識(shí)別出來,作為“灰”色的跟蹤遍歷的起點(diǎn);如果智能指針屬于某個(gè)“黑”色的對(duì)象,如圖12B中的對(duì)象H 1212所示,則當(dāng)對(duì)象H 1212被轉(zhuǎn)成“黑”色的時(shí)候,它的所有引用的對(duì)象必然已經(jīng)轉(zhuǎn)成了“灰”,這其中也包含了對(duì)象H 1211,結(jié)果與假設(shè)發(fā)生矛盾。至此,所有的可能性都進(jìn)行了分析,都導(dǎo)出了與原假設(shè)相反的結(jié)果,所以原假設(shè)不成立。
既然,在步驟1204的時(shí)候,沒有可能存在一個(gè)指向“白”色對(duì)象的引用,那么也就不可能在此之后又有指向“白”色對(duì)象的引用。因?yàn)?,?yīng)用程序保證了賦值操作成功完成之后,原來的指針變量才可能改變或清空(錯(cuò)誤的次序顯然會(huì)令對(duì)象被提前回收),這意味著被引用的對(duì)象在原來的指針變量變化之前就會(huì)轉(zhuǎn)成“灰”色。所以,如果假設(shè)有一個(gè)“白”色對(duì)象將要轉(zhuǎn)成“灰”色,那么必然存在原來的指針變量還未改變或清空,該現(xiàn)存的指針變量根據(jù)上面的分析必然會(huì)被檢測(cè)出來,假設(shè)不成立。
所以,一旦發(fā)生了“灰”色對(duì)象全部處理完了,就不會(huì)再有新的“灰”色對(duì)象出現(xiàn)。(在本次垃圾回收操作過程期間)。
圖13是本實(shí)施例的垃圾回收操作的標(biāo)記階段的數(shù)據(jù)框圖。
在圖13中,有三個(gè)函數(shù)代碼在執(zhí)行,它們是賦值操作1301、創(chuàng)建對(duì)象1302和回收器1303。前兩個(gè)函數(shù)由應(yīng)用程序進(jìn)行調(diào)用,后一個(gè)則由系統(tǒng)執(zhí)行。
排他鎖L1和標(biāo)志F1 1304用于保護(hù)共享數(shù)據(jù),共享數(shù)據(jù)是指被系統(tǒng)線程和應(yīng)用程序線程的共同訪問的數(shù)據(jù),即可能被賦值操作1301和創(chuàng)建對(duì)象1302操作訪問到的回收器的內(nèi)部數(shù)據(jù)。集合SA 1305和SG 1306將賦值操作1301與回收器1303分隔開來,令兩者在大部分時(shí)間里都可以并發(fā)地工作。這兩個(gè)集合存放著應(yīng)該變成“灰”色的“白”色對(duì)象的引用。賦值操作1301只訪問SA 1305,而回收器則只訪問SG 1306并在適當(dāng)?shù)臅r(shí)候交換SG 1306和SA 1305。在標(biāo)記階段期間,創(chuàng)建對(duì)象1302操作只訪問集合SB 1307;在其他時(shí)間, 它可以直接訪問“黑”色對(duì)象的列表LB 1309?;厥掌?303掃描“白”色對(duì)象列表LW 1308并將鎖定的“白”對(duì)象轉(zhuǎn)成“灰”色,移到“灰”對(duì)象列表LG 1310中,并跟蹤遍歷“灰”色對(duì)象將它們轉(zhuǎn)成“黑”色,移入LB 1309中。
使用這樣的數(shù)據(jù)結(jié)構(gòu),引用跟蹤遍歷操作就可以有較小的同步開銷,更加有效率。賦值操作1301和創(chuàng)建對(duì)象1302必須先獲得排他鎖L1 1304,才能訪問集合SA 1305和SB 1307?;厥掌饕脖仨毾全@得L1 1304才能交換SA 1305和SG 1306。
圖14是本實(shí)施例的標(biāo)記階段的流程圖,圖15是賦值操作的流程圖,圖16是創(chuàng)建對(duì)象操作的流程圖。
在圖14中,步驟1401對(duì)應(yīng)圖12A中的步驟1201,它在鎖定L1的情況下,完成標(biāo)志F1的設(shè)置,以便賦值操作和創(chuàng)建對(duì)象的操作能夠檢測(cè)到當(dāng)前回收器的狀態(tài);所有的對(duì)象被切換成“白”色,從LB 1309切換到LW 1308。
步驟1402及隨后的1403、1404、1406等,都屬于圖12A中的步驟1202。步驟1405則屬于步驟1203。首先,步驟1402掃描確定鎖定的“白”色對(duì)象,移入“灰”色對(duì)象列表LG 1310。步驟1403跟蹤遍歷這些“灰”色對(duì)象,將它們?nèi)哭D(zhuǎn)成“黑”色。在此期間,應(yīng)用程序自由地運(yùn)行不會(huì)發(fā)生阻塞,因?yàn)樵摬僮髦簧婕皟?nèi)部的數(shù)據(jù)結(jié)構(gòu),例如LW1308、LB1309和LG1310,并不使用SA1305和SB1307。
在圖15中,如果有賦值操作發(fā)生在此期間,而且該操作的“右值”是一個(gè)“白”色對(duì)象的引用,則執(zhí)行步驟1501,將該“白”色對(duì)象的引用加到SA 1305中,并在L1 1304鎖定的情況下進(jìn)行。
在圖16中,新創(chuàng)建的對(duì)象將作為“黑”色對(duì)象處理,當(dāng)回收器正在運(yùn)行的時(shí)候,新對(duì)象被加到SB 1307中,其他時(shí)間則直接加到LB 1309中。這些操作都在L1 1304的保護(hù)下進(jìn)行。
系統(tǒng)不斷地進(jìn)行跟蹤遍歷的操作直到步驟1405檢測(cè)到“灰”色對(duì)象為空,步驟1404鎖定L1 1304并交換SA1305和SG1306,步驟1406則將SG1306中的對(duì)象進(jìn)行跟蹤遍歷,改為“黑”色。當(dāng)步驟1405檢測(cè)到“灰”色對(duì)象沒有了,則意味著標(biāo)記工作的結(jié)束,該判斷邏輯上是發(fā)生在步驟1404的鎖定狀態(tài)下的,因?yàn)閺牟襟E1404到步驟1405其間,沒有代碼能訪問SG 1306。步驟1407清除了標(biāo)志F1,結(jié)束了標(biāo)記階段,“黑”色對(duì)象列表LB1309再次對(duì)應(yīng)用程序的開放,創(chuàng)建新的對(duì)象可以直接加到該列表中?!鞍住鄙牟豢蛇_(dá)對(duì)象則確定為垃圾對(duì)象,將被回收。
步驟1408在L1 1304的保護(hù)下,將新創(chuàng)建的對(duì)象從SB 1307加回到LB 1309中,如果該操作不能很快完成,則可以逐個(gè)對(duì)象進(jìn)行處理,以保證不會(huì)長(zhǎng)時(shí)間地占據(jù)L1 1304排他鎖。
全部步驟描述完畢,可以發(fā)現(xiàn)在這些步驟中,沒有調(diào)用掛起線程的操作,而且所有獨(dú)占排他鎖L1 1304的時(shí)間都很短,并可以預(yù)見如果發(fā)生競(jìng)爭(zhēng)將導(dǎo)致的最壞的情況。標(biāo)記階段的競(jìng)爭(zhēng)步驟包括步驟1401、1404、1408、1501、1601、1602,這些競(jìng)爭(zhēng)下的操作都是簡(jiǎn)單的、常量復(fù)雜度的。
如果采用Snapshot-at-beginning增量回收方法,可以以本實(shí)施例作為基礎(chǔ)稍加改變完成。區(qū)別主要在于指針的賦值操作將導(dǎo)致對(duì)原來“左值”對(duì)象進(jìn)行簿記,而不是新引用的“右值”對(duì)象。在步驟1501中,如果在標(biāo)記操作期間,賦值操作中原來的“左”值對(duì)象是“白”色,則將該“白”色對(duì)象的引用在L11304的保護(hù)下加到SA 1305中。其他操作基本不變。
在標(biāo)記期間,如果某對(duì)象的引用計(jì)數(shù)值降到0則可以按實(shí)施例一的方法進(jìn)行處理,即建立一個(gè)隊(duì)列將這些零引用的對(duì)象記錄在案,然后在標(biāo)記結(jié)束之后再進(jìn)行回收。這樣,零引用對(duì)象的回收不會(huì)發(fā)生在垃圾回收的過程中,也無須特別的同步機(jī)制進(jìn)行保護(hù),因此整個(gè)系統(tǒng)在發(fā)生競(jìng)爭(zhēng)的最壞情況下,可能造成應(yīng)用程序發(fā)生阻塞的長(zhǎng)度就是上面描述的那些競(jìng)爭(zhēng)步驟,例如1401、1404、1408等。而且,這些競(jìng)爭(zhēng)是多線程之間的標(biāo)準(zhǔn)的競(jìng)爭(zhēng)行為,沒有使用暫停掛起線程的操作,也無須對(duì)所有的線程進(jìn)行掛起操作,只是在兩個(gè)線程發(fā)生競(jìng)爭(zhēng)的情況下,才會(huì)有阻塞,所以整個(gè)系統(tǒng)對(duì)應(yīng)用程序的運(yùn)行影響很小。
另外一種處理零引用對(duì)象方法,則是在垃圾回收的過程中直接進(jìn)行零引用對(duì)象的回收,而不是延遲到標(biāo)記結(jié)束之后才進(jìn)行,下面對(duì)此詳細(xì)說明。
直接完成零引用對(duì)象的回收的好處是它提供了嚴(yán)格的析構(gòu)函數(shù)的執(zhí)行次序。一個(gè)零引用的對(duì)象的析構(gòu),可能導(dǎo)致其他的對(duì)象的引用計(jì)數(shù)降到0,從而遞歸地執(zhí)行析構(gòu)函數(shù)。這樣,析構(gòu)函數(shù)的完成次序就完全與常規(guī)的純引用計(jì)數(shù)的回收方法一致了。換一個(gè)角度,在這樣的實(shí)施例中,當(dāng)一個(gè)對(duì)象的引用數(shù)為0的時(shí)候,系統(tǒng)對(duì)該對(duì)象采取的操作,不應(yīng)該受到正在進(jìn)行中的增量垃圾回收操作的影響。
要達(dá)到這樣的效果,必須在零引用對(duì)象的回收操作與增量垃圾回收操作之間采取同步機(jī)制進(jìn)行協(xié)調(diào)。回收零引用對(duì)象需要使用到回收器的內(nèi)部數(shù)據(jù)結(jié)構(gòu),包括LB1309、LG1310、LW1308,所以回收器和零引用對(duì)象回收操作訪問這些對(duì)象時(shí),需要先鎖定相關(guān)的鎖。此外,當(dāng)回收器調(diào)用對(duì)象的遍歷函數(shù)的期間需要鎖定,以防止并發(fā)地發(fā)生該對(duì)象的回收操作,因?yàn)閷?duì)象的析構(gòu)函數(shù)通常不能與對(duì)象遍歷函數(shù)并發(fā)執(zhí)行。
本實(shí)施例是這樣實(shí)現(xiàn)的,除了L1 1304另外再定義一個(gè)排他鎖,它保護(hù)的對(duì)象包括,LB1309、LG1310、LW1308及對(duì)象的遍歷函數(shù)的調(diào)用。回收器在使用這些數(shù)據(jù)結(jié)構(gòu)之前必須先獲得該鎖,調(diào)用對(duì)象的遍歷函數(shù)也必須在獲得該鎖的情況下進(jìn)行。當(dāng)對(duì)象的引用計(jì)數(shù)為0時(shí),系統(tǒng)首先獲得該排他鎖;然后檢查回收器是否在某些特別的操作下,例如在掃描所有對(duì)象的過程中,如果是這種情況,則需要進(jìn)行相關(guān)的一些處理,保證這些特別的操作在對(duì)象被刪除之后仍能繼續(xù)進(jìn)行,例如判斷對(duì)象掃描的當(dāng)前指針是否正在刪除的對(duì)象,是則該指針改為指向下一個(gè);然后將回收器中各種可能引用該對(duì)象的地方都檢查一遍,將這些引用去除,包括從LB1309、LG1310、LW1308列表中將該對(duì)象去除;最后,回收該對(duì)象。
總的來說,就是當(dāng)一個(gè)對(duì)象的引用數(shù)為0時(shí),代表應(yīng)用程序不再使用該對(duì)象,但是回收器中可能還存在該對(duì)象的引用,必須去除,而且回收器還可能正在使用該對(duì)象,例如執(zhí)行其遍歷函數(shù),必須等待回收器完成使用,而回收器應(yīng)該盡可能將長(zhǎng)的操作改為可中斷的操作。在本實(shí)施例中,最長(zhǎng)的操作就是執(zhí)行對(duì)象的遍歷函數(shù),該函數(shù)由應(yīng)用程序編寫,因此最壞的競(jìng)爭(zhēng)情況可以由應(yīng)用程序確定。系統(tǒng)可以提供服務(wù)允許一個(gè)對(duì)象的遍歷函數(shù)分成多個(gè)部分分別完成,以減小競(jìng)爭(zhēng)的粒度,避免阻塞太長(zhǎng)的時(shí)間。例如,遍歷函數(shù)根據(jù)調(diào)用的參數(shù),決定遍歷部分;或者允許為一個(gè)對(duì)象定義多個(gè)遍歷函數(shù),每個(gè)只完成部分工作。
如果某個(gè)應(yīng)用程序的函數(shù)不涉及內(nèi)存分配的變化,即不創(chuàng)建對(duì)象,也不會(huì)減少對(duì)象的引用數(shù)(或者延遲零引用的回收),則該函數(shù)的執(zhí)行過程受到垃圾回收的影響極小,即使發(fā)生競(jìng)爭(zhēng),導(dǎo)致的阻塞時(shí)間也與對(duì)象的遍歷函數(shù)無關(guān),是可以預(yù)計(jì)的常量。這是目前各種垃圾回收方法無法做到的,也是很有實(shí)際價(jià)值的,例如,某外部事件的處理函數(shù),就通??梢詽M足上述的條件,即無須分配創(chuàng)建對(duì)象,釋放對(duì)象。這些函數(shù)通常都需要較高的實(shí)時(shí)響應(yīng)能力,即使沒有硬的實(shí)時(shí)要求,如果能夠盡快地響應(yīng)也往往會(huì)帶來整體效率的提高。
在這樣的基礎(chǔ)上,還可以作出各種改進(jìn)。例如,可以基于這樣的事實(shí),在回收操作的過程中,對(duì)內(nèi)部數(shù)據(jù)的訪問次數(shù)和頻率要遠(yuǎn)遠(yuǎn)高于零引用對(duì)象的回收,可以設(shè)計(jì)一種同步機(jī)制,令回收操作先鎖定資源,然后在執(zhí)行的過程中,頻繁地檢查應(yīng)用程序是否提出競(jìng)爭(zhēng)請(qǐng)求,是則釋放該鎖,令應(yīng)用程序有機(jī)會(huì)運(yùn)行。這種檢查顯然要比不斷地獲得隨后又釋放排他鎖要快得多。同樣的原理也可以應(yīng)用于其他競(jìng)爭(zhēng)處理方面,只要競(jìng)爭(zhēng)雙方對(duì)共享數(shù)據(jù)的訪問頻率的相差較大就可以收到較好的效果。
其他方面,對(duì)于本領(lǐng)域的技術(shù)人員來說,也可以很輕易地就作出各種修改和變化。
例1,可以將SA 1305、SG 1306和SB 1307去除,允許應(yīng)用程序線程直接訪問修改回收器的各種內(nèi)部數(shù)據(jù),當(dāng)然這樣會(huì)帶來較大的同步開銷,如果同步的粒度較小則回收器運(yùn)行的效率會(huì)較低,相反如果同步的粒度較大,則回收器運(yùn)行的效率提高的同時(shí)造成競(jìng)爭(zhēng)時(shí)的阻塞時(shí)間較長(zhǎng)。
例2,可以不用通過掃描來確定鎖定的對(duì)象,而在運(yùn)行時(shí)標(biāo)識(shí)鎖定的同時(shí)完成鎖定的對(duì)象的集合管理,在垃圾回收操作時(shí)就直接使用該集合作為掃描的結(jié)果。
例3,某些應(yīng)用環(huán)境需要單線程或者準(zhǔn)單線程的對(duì)象模式,例如COM的STA(Single-ThreadedApartment),那么可以將垃圾回收操作作為消息處理函數(shù)完成,或者在另外一個(gè)線程中執(zhí)行,但是將“白”對(duì)象的回收操作在消息處理函數(shù)中完成,不過在這種情況下對(duì)象的遍歷函數(shù)仍需要注意是異步執(zhí)行的。
例4,對(duì)象的附加控制塊,圖3A中的301,并不一定需要與用戶自定義的數(shù)據(jù)結(jié)構(gòu)緊鄰,只要邏輯上關(guān)聯(lián)即可,本文的實(shí)施例僅僅是示范而已,事實(shí)上,附加控制塊可以是一指針,指出真正的有關(guān)控制信息,也可以是與用戶自定義數(shù)據(jù)塊地址相關(guān)聯(lián)的關(guān)聯(lián)數(shù)組的元素,或者其他方式。
例5,引用跟蹤的遍歷方法可以采用各種窮盡(Exhaustive)的遍歷方法,例如在實(shí)施例二的增量引用遍歷的過程中,可以在掃描對(duì)象以確定鎖定的對(duì)象的過程中,插入跟蹤遍歷,不一定需要在掃描完成之后才開始跟蹤遍歷,跟蹤遍歷也不一定需要列表LG 1310,可以使用深度遍歷的遞歸方法等。而三種顏色的增量原則為了邏輯上的表述方便而已,實(shí)際實(shí)現(xiàn)時(shí)不一定需要三種顏色,也可能需要更多的顏色來區(qū)分不同類型的對(duì)象。
例6,可以將引用計(jì)數(shù)部分去除,保留鎖定計(jì)數(shù)器及增量跟蹤回收,從而作為一種增量跟蹤垃圾回收系統(tǒng),該系統(tǒng)仍具備無暫停的實(shí)時(shí)系統(tǒng)的特性。實(shí)施中可以將引用計(jì)數(shù)器的維護(hù)工作去除,保留寫屏障的賦值操作處理部分,并去除零引用對(duì)象回收的相關(guān)代碼即可。
雖然本發(fā)明已以前述優(yōu)選實(shí)例說明,然其并非用于限制本發(fā)明,任何本領(lǐng)域的普通技術(shù)人員,在不脫離本發(fā)明的精神和范圍的情況下,可作各種的更動(dòng)與修改。因此本發(fā)明的保護(hù)范圍以所附權(quán)利要求為準(zhǔn)。
權(quán)利要求
1.一種計(jì)算機(jī)內(nèi)存的垃圾回收方法,其特征是應(yīng)用程序在運(yùn)行時(shí)自動(dòng)標(biāo)識(shí)出被“擴(kuò)展根集”中指針?biāo)玫膶?duì)象,而不是描述根集中的指針或引用本身及其分布;系統(tǒng)在垃圾回收時(shí)則不必掃描根集,以確定其中的指針,而直接以應(yīng)用程序動(dòng)態(tài)標(biāo)識(shí)的對(duì)象作為起點(diǎn),展開引用跟蹤回收。
2.根據(jù)權(quán)利要求1所述的垃圾回收方法,其特征在于使用引用計(jì)數(shù)的方式標(biāo)識(shí)出被“擴(kuò)展根集”指針?biāo)玫膶?duì)象,系統(tǒng)為每個(gè)接受本系統(tǒng)管理的對(duì)象(簡(jiǎn)稱對(duì)象)維護(hù)與之相關(guān)的引用計(jì)數(shù)器,稱為鎖定計(jì)數(shù)器;鎖定計(jì)數(shù)器的值反映代表了來自“擴(kuò)展根集”的、指向該對(duì)象的引用數(shù)目,引用數(shù)目(即鎖定計(jì)數(shù)器)非空則意味該對(duì)象被標(biāo)識(shí),進(jìn)而作為系統(tǒng)在進(jìn)行引用跟蹤回收時(shí)的起始對(duì)象。
3.根據(jù)權(quán)利要求1所述的垃圾回收方法,其特征在于系統(tǒng)為每個(gè)受管理對(duì)象維護(hù)一個(gè)數(shù)據(jù)結(jié)構(gòu),該結(jié)構(gòu)通過公知的集合管理手段,包括鏈表、數(shù)組、向量、聚集、集合、圖、哈希表和及其組合,描述受管理對(duì)象被“擴(kuò)展根集”中指針引用的狀態(tài),每個(gè)這樣的結(jié)構(gòu)應(yīng)該作為集合的一個(gè)元素,而不是每個(gè)指針或者引用作為一個(gè)元素,并且在系統(tǒng)跟蹤回收時(shí),無須掃描根集以確定跟蹤的起始對(duì)象。
4.根據(jù)權(quán)利要求2所述的垃圾回收方法,其特征在于運(yùn)行的時(shí)候,系統(tǒng)維護(hù)一個(gè)起始對(duì)象集合,將鎖定計(jì)數(shù)器的值為正數(shù)的對(duì)象加入到該集合中,在對(duì)象的鎖定計(jì)數(shù)器的值為0時(shí),將該對(duì)象從起始對(duì)象集合中剔除;在進(jìn)行引用跟蹤回收的時(shí)候,系統(tǒng)直接使用該起始對(duì)象集合中的對(duì)象作為引用跟蹤的起點(diǎn)。
5.根據(jù)權(quán)利要求2或4所述的垃圾回收方法,其特征在于包括以下的步驟,步驟1,將引用某對(duì)象的指針分成若干類,其中一類是賦值和初始化過程不應(yīng)造成“右值”對(duì)象的引用計(jì)數(shù)值發(fā)生變化的;步驟2,賦值的時(shí)候,將“左值”原來所指的對(duì)象的引用計(jì)數(shù)器自減,“左值”變量指向“右值”對(duì)象,“右值”變量清空,這里的清空是指該變量的值改為某預(yù)定的固定值或者動(dòng)態(tài)可確定的數(shù)值集合元素之一;步驟3,指針初始化的時(shí)候與步驟2相同,區(qū)別在于因?yàn)椤白笾怠睘槲闯跏蓟臒o效值,不進(jìn)行“左值”對(duì)象的引用計(jì)數(shù)器自減操作;步驟4,當(dāng)指針超出作用域時(shí),所指的對(duì)象的引用計(jì)數(shù)器進(jìn)行自減操作。
6.根據(jù)權(quán)利要求5所述的垃圾回收方法,其特征在于除了鎖定計(jì)數(shù)器外,還有引用計(jì)數(shù)器代表來自其他指針的引用個(gè)數(shù);當(dāng)鎖定計(jì)數(shù)器和引用計(jì)數(shù)器的值均為零的時(shí)候,系統(tǒng)回收該對(duì)象。并且在適當(dāng)?shù)臅r(shí)候,可以通過引用跟蹤回收方法將循環(huán)引用的垃圾對(duì)象回收。
7.根據(jù)權(quán)利要求2或4所述的垃圾回收方法,其特征在于采用增量的引用跟蹤回收;應(yīng)用程序在鎖定計(jì)數(shù)器的維護(hù)操作的期間(或之前、之后的緊鄰操作),實(shí)施寫屏障及(或)多線程同步機(jī)制,與增量垃圾回收器進(jìn)行并發(fā)多線程的協(xié)同工作。
8.根據(jù)權(quán)利要求7所述的垃圾回收方法,其特征在于增量的引用跟蹤回收操作的過程中發(fā)生的賦值、初始化及等同操作,其相關(guān)對(duì)象如果是“白”色則移入“灰”色對(duì)象集合,等待跟蹤遍歷;新創(chuàng)建的對(duì)象則作為“黑”色處理;回收器將完成跟蹤遍歷之后的“灰”色對(duì)象移入“黑”色集合,并且在無須掛起所有相關(guān)的應(yīng)用程序線程,或者阻塞所有相關(guān)的應(yīng)用程序線程,以達(dá)到全局的引用關(guān)系圖一致的情況下,通過判斷是否存在“灰”色對(duì)象,作為“完成確定垃圾對(duì)象的工作”的標(biāo)準(zhǔn)。
9.一種計(jì)算機(jī)自動(dòng)內(nèi)存管理系統(tǒng),其特征是應(yīng)用程序在運(yùn)行時(shí)自動(dòng)標(biāo)識(shí)“擴(kuò)展根集”指針?biāo)玫膶?duì)象,在回收操作期間,如果“擴(kuò)展根集”中的指針引用受管理對(duì)象的情況發(fā)生了實(shí)質(zhì)性的變化,則立即引起回收器相關(guān)代碼的執(zhí)行,該代碼將與回收器的跟蹤遍歷代碼以多線程并發(fā)方式執(zhí)行,回收器的跟蹤遍歷代碼不依賴根集掃描來確定遍歷的起點(diǎn),不需要暫停應(yīng)用程序線程來判斷遍歷的結(jié)束,系統(tǒng)維護(hù)對(duì)象的引用數(shù)目,當(dāng)引用數(shù)為零時(shí)可以立即回收該對(duì)象,即使在引用跟蹤的遍歷過程中。
10.根據(jù)權(quán)利要求9所述的系統(tǒng),其特征在于系統(tǒng)使用引用計(jì)數(shù)的方式標(biāo)識(shí)被“擴(kuò)展根集”指針?biāo)玫膶?duì)象,在引用跟蹤回收過程中,當(dāng)根集中的指針的內(nèi)容發(fā)生變化的時(shí)候,引起系統(tǒng)將這些指針?biāo)婕暗摹鞍住鄙珜?duì)象進(jìn)行簿記,在此后的跟蹤回收時(shí)作為“灰”色對(duì)象處理;在跟蹤回收期間新創(chuàng)建的對(duì)象則作為“黑”色處理;回收器將完成跟蹤遍歷之后的“灰”色對(duì)象移入“黑”色集合,當(dāng)沒有“灰”色對(duì)象等待遍歷則完成垃圾對(duì)象的確認(rèn)工作。
全文摘要
本發(fā)明涉及一種計(jì)算機(jī)自動(dòng)內(nèi)存管理技術(shù),又稱垃圾回收機(jī)制。該技術(shù)可以應(yīng)用在常規(guī)的C++語言環(huán)境或其他環(huán)境下,提供精確的引用跟蹤回收,支持隱藏的指針,以及對(duì)本地對(duì)象的跟蹤;結(jié)合引用計(jì)數(shù)和引用跟蹤的特點(diǎn),提供確定性的對(duì)象析構(gòu),統(tǒng)一了資源管理和對(duì)象管理,令資源和內(nèi)存在失去最后一個(gè)引用的同時(shí)立即得到釋放;提供徹底無暫停的增量垃圾回收,可預(yù)測(cè)最壞的情況,適用于實(shí)時(shí)響應(yīng)的操作系統(tǒng)內(nèi)核;提供高效率的內(nèi)存管理,僅僅只是循環(huán)引用的垃圾對(duì)象浪費(fèi)了內(nèi)存,精心設(shè)計(jì)的應(yīng)用程序可以完全不需要垃圾回收;運(yùn)行時(shí)的管理開銷遠(yuǎn)低于常規(guī)的引用計(jì)數(shù)方法。
文檔編號(hào)G06F9/50GK101046755SQ200610034590
公開日2007年10月3日 申請(qǐng)日期2006年3月28日 優(yōu)先權(quán)日2006年3月28日
發(fā)明者郭明南 申請(qǐng)人:郭明南
網(wǎng)友詢問留言 已有0條留言
  • 還沒有人留言評(píng)論。精彩留言會(huì)獲得點(diǎn)贊!
1