基于內(nèi)存掃描的嵌入式軟件動(dòng)態(tài)內(nèi)存回收方法
【技術(shù)領(lǐng)域】
[0001]本發(fā)明涉及內(nèi)存管理技術(shù)領(lǐng)域,尤其涉及一種基于內(nèi)存掃描的嵌入式軟件動(dòng)態(tài)內(nèi)存回收方法。
【背景技術(shù)】
[0002]在軟件開發(fā)過程中,內(nèi)存管理是一個(gè)復(fù)雜的問題,對(duì)于功能復(fù)雜的程序而言,使用動(dòng)態(tài)分配是不可避免的。因?yàn)槎鄠€(gè)功能所使用的內(nèi)存數(shù)量一直會(huì)變化,某些情況下會(huì)使用較多的內(nèi)存,另一些情況下使用較少的內(nèi)存。一般動(dòng)態(tài)分配的內(nèi)存都由操作系統(tǒng)來管理,從一個(gè)叫做“堆(Heap) ”的內(nèi)存區(qū)域分配,當(dāng)程序調(diào)用接口函數(shù)申請(qǐng)內(nèi)存時(shí),操作系統(tǒng)從堆空間中找到一塊空閑區(qū)域,分配給程序;當(dāng)程序釋放內(nèi)存時(shí),操作系統(tǒng)再把內(nèi)存塊標(biāo)記為空閑,可以供重新分配。對(duì)于設(shè)計(jì)不良的程序,其中可能會(huì)有之前分配的動(dòng)態(tài)內(nèi)存已經(jīng)不用,但是沒有釋放的情況,這就是所謂的“內(nèi)存泄漏(Memory Leak)”。一旦長(zhǎng)期運(yùn)行的程序中存在內(nèi)存泄漏,堆內(nèi)存不斷被分配出去而不被釋放,久而久之,可用內(nèi)存越來越少,最終,再也無(wú)法成功分配出內(nèi)存,導(dǎo)致功能失效,甚至裝置崩潰。
[0003]內(nèi)存回收就是將程序中已經(jīng)不使用但是又沒釋放的動(dòng)態(tài)內(nèi)存塊自動(dòng)釋放的過程。在內(nèi)存回收后,不被使用的內(nèi)存塊被歸還到堆中,可再次被動(dòng)態(tài)分配,從而避免內(nèi)存泄漏情況的出現(xiàn)。
[0004]目前在嵌入式系統(tǒng)中進(jìn)行內(nèi)存回收可用的方案包括:
I)使用支持內(nèi)存回收的語(yǔ)言開發(fā)
目前,支持垃圾回收的語(yǔ)言包括JAVA、.NET等,或者Python等腳本語(yǔ)言。它們的特點(diǎn)是程序并不被編譯成特定CPU的機(jī)器碼,而是被編譯成一種中間代碼,然后由一段解釋程序在運(yùn)行時(shí)動(dòng)態(tài)將中間代碼翻譯為機(jī)器碼執(zhí)行,這段解釋程序被稱作虛擬機(jī)(VM)。在程序運(yùn)行過程中,虛擬機(jī)會(huì)實(shí)時(shí)管理內(nèi)存分配和釋放的情況,對(duì)于已經(jīng)不引用的內(nèi)存,就自動(dòng)釋放掉,這樣就避免了內(nèi)存泄漏的情況。不同的語(yǔ)言使用的內(nèi)存回收策略也有所不同。
[0005]使用支持垃圾回收的語(yǔ)言開發(fā),就可以在很大程度上降低內(nèi)存管理的復(fù)雜性,開發(fā)者僅需在需要分配內(nèi)存的時(shí)候分配即可,而將釋放內(nèi)存的過程交給語(yǔ)言本身的內(nèi)存回收機(jī)制完成,從而避免了內(nèi)存泄漏的情況。
[0006]這種方式的缺點(diǎn)在于:解釋執(zhí)行的語(yǔ)言在性能上比編譯為機(jī)器代碼執(zhí)行的語(yǔ)言要低,加之嵌入式設(shè)備硬件性能有限,在實(shí)時(shí)性要求比較高的場(chǎng)合并不適用。
[0007]2)使用智能指針管理內(nèi)存
在C++中,由于語(yǔ)言本身沒有提供內(nèi)存回收機(jī)制,為了降低內(nèi)存管理的復(fù)雜度,引入了“智能指針”的概念。智能指針的概念是和“裸指針”相對(duì)應(yīng)的,裸指針就是傳統(tǒng)的指針,表示的是內(nèi)存的地址;而智能指針則是經(jīng)過封裝的對(duì)象,它除了保存了內(nèi)存地址外,還保存了一些諸如引用計(jì)數(shù)之類的附加信息。智能指針的實(shí)現(xiàn)使用了 C++語(yǔ)言的特性,在智能指針析構(gòu)時(shí),會(huì)自動(dòng)修改其所指向的內(nèi)存塊的引用計(jì)數(shù),當(dāng)引用計(jì)數(shù)變?yōu)镺時(shí),就將指向的內(nèi)存塊釋放掉。其由于智能指針重載了傳統(tǒng)指針具備的幾種運(yùn)算符(如*、->等),因此在編程中基本無(wú)需特別關(guān)心智能指針的操作,只需按照傳統(tǒng)指針使用即可。
[0008]目前在常用的C++開發(fā)框架中均提供了智能指針實(shí)現(xiàn),例如boost,QT等。
[0009]這種機(jī)制的缺點(diǎn)是引入了自定義的內(nèi)存管理機(jī)制,增加了開發(fā)時(shí)的工作量。無(wú)法管理在第三方庫(kù)中分配的動(dòng)態(tài)內(nèi)存,難免出現(xiàn)內(nèi)存泄漏。此外,智能指針的實(shí)現(xiàn)使用了 C++的語(yǔ)言特性,在某些僅能使用C語(yǔ)言的場(chǎng)合無(wú)法使用。
[0010]3)使用基于所有者的對(duì)象樹管理機(jī)制
由于在實(shí)際使用的情況中,程序中動(dòng)態(tài)分配的對(duì)象一般在生存期上有依存關(guān)系,子對(duì)象的生存期一般不會(huì)超過父對(duì)象的生存期,所以將程序動(dòng)態(tài)分配的對(duì)象按依存關(guān)系組織成一個(gè)對(duì)象樹。當(dāng)父對(duì)象析構(gòu)時(shí),將父對(duì)象的所有子對(duì)象全部釋放。這樣,應(yīng)用程序就只需管理好處于頂層對(duì)象的生存期即可,大大降低了由于程序中的失誤造成內(nèi)存泄漏的可能性。
[0011]這種機(jī)制的缺點(diǎn)與方法2)中相同,由于引入了自定義的管理機(jī)制,因此無(wú)法用來管理第三方庫(kù)中分配的動(dòng)態(tài)內(nèi)存。
【發(fā)明內(nèi)容】
[0012]針對(duì)上述方案存在的程序執(zhí)行性能低、軟件開發(fā)工作量大和無(wú)法管理第三方代碼庫(kù)分配的動(dòng)態(tài)內(nèi)存等問題,本發(fā)明提供了一種基于內(nèi)存掃描的嵌入式軟件動(dòng)態(tài)內(nèi)存回收方法,在不額外增加開發(fā)工作量的前提下,實(shí)現(xiàn)動(dòng)態(tài)內(nèi)存的有效回收,并能兼容任何第三方庫(kù)。
[0013]為達(dá)到上述目的,本發(fā)明采用了內(nèi)存掃描的方式。在C/C++語(yǔ)言中,對(duì)內(nèi)存的訪問都是通過指針進(jìn)行的。所謂指針,就是某個(gè)數(shù)據(jù)或?qū)ο笤趦?nèi)存中的地址值。每個(gè)動(dòng)態(tài)分配的內(nèi)存塊也都有自己的地址,當(dāng)需要訪問內(nèi)存塊中的數(shù)據(jù)時(shí),也是通過指向該內(nèi)存塊的指針來訪問。
[0014]在應(yīng)用程序中,動(dòng)態(tài)分配內(nèi)存后,會(huì)將分配到的內(nèi)存塊的地址保存下來,以備后續(xù)操作中用來訪問內(nèi)存塊中的數(shù)據(jù)。因此,如果我們通過對(duì)程序能訪問到的整塊內(nèi)存進(jìn)行掃描,找不到有指向某個(gè)動(dòng)態(tài)內(nèi)存塊的指針值,則可以斷定,這塊動(dòng)態(tài)內(nèi)存塊已經(jīng)無(wú)法訪問,可以將其釋放。
[0015]結(jié)合上述構(gòu)思,本發(fā)明采用的具體技術(shù)方案如下:
基于內(nèi)存掃描的嵌入式軟件動(dòng)態(tài)內(nèi)存回收方法,包括以下步驟:
(1)確定軟件程序能訪問到的內(nèi)存區(qū)域;
(2)接管程序/系統(tǒng)動(dòng)態(tài)內(nèi)存的分配和釋放過程,分配動(dòng)態(tài)內(nèi)存時(shí),將分配到的動(dòng)態(tài)內(nèi)存塊加入動(dòng)態(tài)內(nèi)存塊集合中;釋放內(nèi)存時(shí),將其從動(dòng)態(tài)內(nèi)存塊集合中移除;
(3)滿足觸發(fā)掃描條件時(shí),開始掃描可訪問的內(nèi)存區(qū)域,尋找內(nèi)存地址范圍在動(dòng)態(tài)內(nèi)存塊集合的內(nèi)存塊范圍中但未標(biāo)記為可訪問的內(nèi)存塊,將其標(biāo)記為可訪問并加入可訪問內(nèi)存塊集合;再循環(huán)掃描可訪問內(nèi)存塊集合中的內(nèi)存塊對(duì)應(yīng)區(qū)域,尋找并繼續(xù)向可訪問內(nèi)存塊集合中增加符合上述條件的內(nèi)存塊,直至可訪問內(nèi)存塊集合中的內(nèi)存塊全被掃描過;
(4)將動(dòng)態(tài)內(nèi)存塊集合與可訪問內(nèi)存塊集合比較,釋放所有不在可訪問內(nèi)存塊集合中的動(dòng)態(tài)內(nèi)存塊;
(5)繼續(xù)執(zhí)行步驟(3)、(4),直到收到退出指令后停止。
[0016]經(jīng)過上述掃描過程后,凡在可訪問內(nèi)存塊集合中的動(dòng)態(tài)內(nèi)存塊,均為在程序中有可能被引用的內(nèi)存塊,而不在可訪問內(nèi)存塊集合中的動(dòng)態(tài)內(nèi)存塊,就是不會(huì)被訪問的內(nèi)存塊,由于該內(nèi)存塊未被釋放,說明該內(nèi)存塊在程序中沒有顯示被釋放,則將其自動(dòng)釋放掉。
[0017]單獨(dú)建立一個(gè)線程定時(shí)對(duì)整塊內(nèi)存進(jìn)行上述掃描,將無(wú)法訪問的內(nèi)存塊釋放掉,則在應(yīng)用程序中可以無(wú)需關(guān)心動(dòng)態(tài)內(nèi)存的釋放,顯著降低開發(fā)工作量。同時(shí),如果程序中引用了第三方的代碼庫(kù),也可以將第三方代碼庫(kù)中分配的內(nèi)存塊也納入管理,同時(shí),第三方代碼庫(kù)中原有的分配釋放機(jī)制也無(wú)需改動(dòng),即便第三方庫(kù)中有內(nèi)存泄漏的缺陷,在內(nèi)存掃描過程中,也可以檢測(cè)到無(wú)法訪問到的內(nèi)存塊并自動(dòng)釋放。
[0018]進(jìn)一步,步驟(2)和步驟(3)之間還包括以下步驟:等待觸發(fā)內(nèi)存掃描的條件,滿足觸發(fā)條件前,內(nèi)存掃描任務(wù)處于掛起狀態(tài)。所述觸發(fā)條件為根據(jù)嵌入式系統(tǒng)整體資源情況和/或CPU性能確定的掃描頻率。內(nèi)存掃描的頻率可根據(jù)嵌入式系統(tǒng)整體的資源情況來確定,在內(nèi)存比較寬裕的系統(tǒng)上,可以使用較低的掃描頻率;在內(nèi)存緊張的系統(tǒng)上可以使用較高的掃描頻率;如果CPU性能較低,為了減少內(nèi)存掃描任務(wù)對(duì)其它功能的影響,可以不定時(shí)掃描,而在可用內(nèi)存不足時(shí)開啟掃描。
[0019]進(jìn)一步,步驟(I)中軟件程序能訪問到的內(nèi)存區(qū)域包括堆、棧和靜態(tài)內(nèi)存區(qū)域。
[0020]優(yōu)選的,步驟(2)接管程序動(dòng)態(tài)內(nèi)存的分配和釋放過程采用宏定義方式,將程序中調(diào)用的內(nèi)存分配和釋放函數(shù)重定向到自定義的函數(shù)中。這種方式僅僅在包含了此宏定義的代碼中有效,對(duì)系統(tǒng)庫(kù)及引用的第三方庫(kù)無(wú)影響。
[0021]優(yōu)選的,步驟(2)接管系統(tǒng)動(dòng)態(tài)內(nèi)存的分配和釋放過程通過修改系統(tǒng)中內(nèi)存分配和釋放的機(jī)器代碼,將執(zhí)行流跳轉(zhuǎn)到自定義的函數(shù)中。這種方式可以接管應(yīng)用程序、系統(tǒng)庫(kù)及第三方庫(kù)中所有的內(nèi)存分配和釋放過程。
[0022]步驟(3)的具體步驟為:
(3-1)掃描靜態(tài)內(nèi)存區(qū)域:對(duì)靜態(tài)內(nèi)存區(qū)域的每個(gè)以指針類型大小對(duì)齊的內(nèi)存地址,均當(dāng)做指針讀出其取值,如果取值范圍在動(dòng)態(tài)內(nèi)存塊集合中的某一內(nèi)存塊范圍內(nèi),且該內(nèi)存塊未被標(biāo)記為可訪問,則將該內(nèi)存塊標(biāo)記為可訪問,并將該內(nèi)存塊加入可訪問內(nèi)存塊集合;
(3-2)掃描棧內(nèi)存區(qū)域:對(duì)棧內(nèi)存區(qū)域的每個(gè)以指針類型大小對(duì)齊的內(nèi)存地址,均當(dāng)做指針讀出其取值,如果取值范圍在動(dòng)態(tài)內(nèi)存塊集合中的某一內(nèi)存塊范圍內(nèi),且該內(nèi)存塊未被標(biāo)記為可訪問,則將該內(nèi)存塊標(biāo)記為可訪問,并將該內(nèi)存塊加入可訪問內(nèi)存塊集合;
(3-3 )掃描可訪問內(nèi)存塊集合中的內(nèi)存塊:對(duì)可訪問內(nèi)存