揭穿號(hào)稱內(nèi)存占用極低軟件的詭計(jì)

字號(hào):

物理內(nèi)存和虛擬內(nèi)存
    物理內(nèi)存,在應(yīng)用中,自然是顧名思義,物理上,真實(shí)的插在板子上的內(nèi)存是多大就是多大了.看機(jī)器配置的時(shí)候,看的就是這個(gè)物理內(nèi)存.  
    如果執(zhí)行的程序很大或很多,就會(huì)導(dǎo)致物理內(nèi)存消耗殆盡.為了解決這個(gè)問(wèn)題,Windows中運(yùn)用了虛擬內(nèi)存技術(shù),即拿出一部分硬盤空間來(lái)充當(dāng)內(nèi)存使用,當(dāng)內(nèi)存占用完時(shí),電腦就會(huì)自動(dòng)調(diào)用硬盤來(lái)充當(dāng)內(nèi)存,以緩解內(nèi)存的緊張.
    一個(gè)程序,不可避免地要用到虛擬內(nèi)存,因?yàn)椴活l繁執(zhí)行或者已經(jīng)很久沒有執(zhí)行的代碼,沒有必要留在物理內(nèi)存中,只會(huì)造成浪費(fèi);放在虛擬內(nèi)存中,等執(zhí)行這部分代碼的時(shí)候,再調(diào)出來(lái).
    Windows 的任務(wù)管理器可以幫助我們看到進(jìn)程的虛擬內(nèi)存.調(diào)出任務(wù)管理器,點(diǎn)擊菜單“查看”-“選擇列”,在出現(xiàn)的窗口中,鉤上“虛擬內(nèi)存大小”.
    點(diǎn)“確定”,這個(gè)時(shí)候,進(jìn)程列表中已經(jīng)顯示各進(jìn)程的虛擬內(nèi)存大小,如圖2:  
    一個(gè)程序到底應(yīng)該使用多少虛擬內(nèi)存呢?不一定,但是應(yīng)該以恰到好處的符合虛擬內(nèi)存原本作用為.
    下面將揭穿表面看起來(lái)調(diào)用了大量圖片、大量運(yùn)行庫(kù)的程序,為什么才“占用”不到 1 MB 的內(nèi)存的詭計(jì).
    原來(lái)是 SetProcessWorkingSetSize 函數(shù)  
    MSDN 對(duì)該函數(shù)的表述(翻譯):使用這個(gè)函數(shù)來(lái)設(shè)置應(yīng)用程序最小和的運(yùn)行空間,只會(huì)保留需要的內(nèi)存.當(dāng)應(yīng)用程序被閑置或系統(tǒng)內(nèi)存太低時(shí),操作系統(tǒng)會(huì)自動(dòng)調(diào)用這個(gè)機(jī)制來(lái)設(shè)置應(yīng)用程序的內(nèi)存.應(yīng)用程序也可以使用 VirtualLock 來(lái)鎖住一定范圍的內(nèi)存不被系統(tǒng)釋放;當(dāng)你加大運(yùn)行空間給應(yīng)用程序,你能夠得到的物理內(nèi)存取決于系統(tǒng),這會(huì)造成其他應(yīng)用程序降低性能或系統(tǒng)總體降低性能,這也可能導(dǎo)致請(qǐng)求物理內(nèi)存的操作失敗,例如:建立 進(jìn)程,線程,內(nèi)核池,就必須小心的使用該函數(shù).  
    也就是說(shuō),該函數(shù)不是節(jié)省內(nèi)存,而是強(qiáng)制把進(jìn)程的物理內(nèi)存搬到虛擬內(nèi)存中.  
    另外有一些資料上說(shuō),該函數(shù)“將有可能導(dǎo)致缺頁(yè)中斷,嚴(yán)重影響性能”.
    函數(shù)原型:
    BOOL SetProcessWorkingSetSize(
    HANDLE hProcess,
    SIZE_T dwMinimumWorkingSetSize,
    SIZE_T dwMaximumWorkingSetSize
    );
    我們用 VB 來(lái)做這么一個(gè)簡(jiǎn)單的例子,是程序占用 300 KB 內(nèi)存吧.  
    建立一個(gè)標(biāo)準(zhǔn)的 VB 工程,在 Form1 中放置一個(gè) Timer1 ,把 Interval 屬性設(shè)置為 1000 (即 1 秒).然后在代碼編輯框中輸入以下代碼:  
    Private Declare Function SetProcessWorkingSetSize Lib "kernel32" (ByVal hProcess As Long, ByVal dwMinimumWorkingSetSize As Long, ByVal dwMaximumWorkingSetSize As Long) As Long
    Private Declare Function GetCurrentProcess Lib "kernel32" () As Long
    Private Sub Timer1_Timer()
    SetProcessWorkingSetSize GetCurrentProcess(), 50000, 100000
    End Sub
    然后生成 工程1.exe,執(zhí)行,調(diào)出任務(wù)管理器查看,發(fā)現(xiàn)內(nèi)存占用才 320 KB.如果把定時(shí)器關(guān)閉,這進(jìn)程的內(nèi)存一般 4 MB左右.
    必須定時(shí)執(zhí)行該函數(shù),否則虛擬內(nèi)存會(huì)慢慢被調(diào)出來(lái),恢復(fù)原來(lái)的內(nèi)存大小.
    如果要使一個(gè)本來(lái)需要占用大量?jī)?nèi)存的程序減低到幾百 KB ,使用同樣的方法即可.
    詭計(jì)帶來(lái)的危害  
    如果 SetProcessWorkingSetSize 函數(shù)被正常使用,是非常有用處的.但是為了蒙騙用戶的眼睛,每秒,甚至幾十毫秒就把大量?jī)?nèi)存往虛擬內(nèi)存里面壓,就會(huì)帶來(lái)無(wú)可預(yù)計(jì)的危害.看看這篇文章怎么說(shuō):“因?yàn)樗皇菚簳r(shí)的將應(yīng)用程序占用的內(nèi)存移至虛擬內(nèi)存,一旦,應(yīng)用程序被激活或者有操作請(qǐng)求時(shí),這些內(nèi)存又會(huì)被重新占用.如果你強(qiáng)制使用該方法來(lái)設(shè)置程序占用的內(nèi)存,那么可能在一定程度上反而會(huì)降低系統(tǒng)性能,因?yàn)橄到y(tǒng)需要頻繁的進(jìn)行內(nèi)存和硬盤間的頁(yè)面交換.”.  
    沒錯(cuò),如果你使用了這類軟件,意味著你的硬盤將每秒將 I/O 大量數(shù)據(jù);硬盤的磁針將拼命旋轉(zhuǎn)...(當(dāng)然硬盤磁針不可能不旋轉(zhuǎn)^_^,只是選擇得更厲害而已).  
    不是說(shuō) BT 很傷內(nèi)存嗎?不然,因?yàn)楝F(xiàn)在大多 BT 軟件都有緩存技術(shù).且看 Bitcomet 官方對(duì)緩存技術(shù)的說(shuō)明:“傳統(tǒng)BT高速下載時(shí)硬盤會(huì)響得很厲害,這是大量的隨機(jī)讀取造成的.... BitComet可以由用戶設(shè)置緩存大小.... 可以明顯地看出犧牲一小部分內(nèi)存作緩存對(duì)硬盤的保護(hù)作用.”  
    是不是有種心寒的感覺?一類軟件寧愿犧牲內(nèi)存,也要減少保護(hù)硬盤;而另外一類軟件,卻為了欺騙用戶,讓CPU、硬盤更加奔波......  
    抓一個(gè)兇手  
    這類軟件不少,我以其中一個(gè)桌面工具為例,揭穿它的假面具(不點(diǎn)名字了).運(yùn)行該軟件后,隨意操作一下,然后打開進(jìn)程管理器,把虛擬內(nèi)存列調(diào)出來(lái),找到該進(jìn)程.
    OK,20 MB 虛擬內(nèi)存,而只有 632 KB 物理內(nèi)存.細(xì)心的你會(huì)發(fā)現(xiàn),大概每 1 秒,該行都有閃爍的感覺,沒錯(cuò),這正是每秒調(diào)用 SetProcessWorkingSetSize 的結(jié)果.另外,我們打開 Norton Process Viewer ,查看該進(jìn)程的 CPU 占用情況. 
    可以看到,就算沒有操作該軟件,但是每秒,都有 3% 的CPU占用起伏(雖然這并不能說(shuō)明什么).另外,內(nèi)存框中可以看到物理內(nèi)存和虛擬內(nèi)存的占用,兩者相去甚遠(yuǎn).此外,可以用 Hook API 技術(shù)來(lái)證明每秒調(diào)用 SetProcessWorkingSetSize 的行為.  
    應(yīng)該怎么做  
    這篇文章只想讓用戶了解軟件占用資源的實(shí)際.而程序員應(yīng)該把下功夫,真正從代碼中減少內(nèi)存的消耗,而不是一味忽悠用戶.調(diào)用 SetProcessWorkingSetSize 會(huì)帶來(lái)某些好處,但是何時(shí)調(diào)用、如何調(diào)用應(yīng)該符合兩個(gè)要求:
    1,在程序暫時(shí)不被使用的時(shí)候(例如最小化);
    2,物理內(nèi)存和虛擬內(nèi)存應(yīng)處于一個(gè)合適的比例(而不是 600 KB 比 20 MB 這么荒唐);
    3,或者不調(diào)用,讓 Windows 去處理.