用SetProcessWorkingSetSize降低內存使用


在項目中對程序性能優化時,發現用SetProcessWorkingSetSize() 方法使內存降低了很多,於是查閱了相關的資料如下。

一 SetProcessWorkingSetSize 的工作原理

以下來自:http://blog.csdn.net/zlt982001/archive/2005/08/28/466879.aspx

那麼我的程序爲什麼能夠將佔用的內存移至虛擬內存呢?

其實,你也可以,試試看把一個程序最小化到任務欄,再看看任務管理器,看到沒,你的程序佔用的實際內存一下子減少 了,看來並不是我有什麼方法能夠壓縮內存,而是操作系統本身就有這個機制,即當程序不使用時(最小化),操作系統會調用某些命令,來將該程序佔用的內存移 至虛擬內存,只保留一小部分常規代碼

所以我們就看到了 這種情景,佔用的內存一下子就縮小了。

那麼:系統到底調用了什麼指令呢?能不能在不縮小窗體的情況下來釋放內存呢?

看看這個API                       SetProcessWorkingSetSize

這是從MSDN摘下的原話

Using the SetProcessWorkingSetSize function to set an application's minimum and maximum working set sizes does not guarantee that the requested memory will be reserved, or that it will remain resident at all times. When the application is idle, or a low-memory situation causes a demand for memory, the operating system can reduce the application's working set. An application can use the VirtualLock function to lock ranges of the application's virtual address space in memory; however, that can potentially degrade the performance of the system.

使用這個函數來設置應用程序最小和最大的運行空間,只會保留需要的內存。當應用程序被閒置或系統內存太低時,操作系統會自動調用這個機制來設置應用程序的內存。應用程序也可以使用 VirtualLock 來鎖住一定範圍的內存不被系統釋放。

When you increase the working set size of an application, you are taking away physical memory from the rest of the system. This can degrade the performance of other applications and the system as a whole. It can also lead to failures of operations that require physical memory to be present; for example, creating processes, threads, and kernel pool. Thus, you must use the SetProcessWorkingSetSize function carefully. You must always consider the performance of the whole system when you are designing an application.

當你加大運行空間給應用程序,你能夠得到的物理內存取決於系統,這會造成其他應用程序降低性能或系統總體降低性能,這也可能導致請求物理內存的操作失敗,例如:建立 進程,線程,內核池,就必須小心的使用該函數。

========================

事實上,使用該函數並不能提高什麼性能,也不會真的節省內存。

因爲他只是暫時的將應用程序佔用的內存移至虛擬內存,一旦,應用程序被激活或者有操作請求時,這些內存又會被重新佔用。如果你強制使用該方法來 設置程序佔用的內存,那麼可能在一定程度上反而會降低系統性能,因爲系統需要頻繁的進行內存和硬盤間的頁面交換。


BOOL SetProcessWorkingSetSize(
HANDLE hProcess,
SIZE_T dwMinimumWorkingSetSize,
SIZE_T dwMaximumWorkingSetSize
);


將 2個 SIZE_T 參數設置爲 -1 ,即可以使進程使用的內存交換到虛擬內存,只保留一小部分代碼

而桌面日曆秀 之所以能夠 總是保持 最小內存,是因爲使用了定時器,不停的進行該操作,,所以性能可想而知,雖然換來了小內存的假象,對系統來說確實災難。

當然,該函數也並非無一是處,

1 。當我們的應用程序剛剛加載完成時,可以使用該操作一次,來將加載過程不需要的代碼放到虛擬內存,這樣,程序加載完畢後,保持較大的可用內存。VB尤甚

2.程序運行到一定時間後或程序將要被閒置時,可以使用該命令來交換佔用的內存到虛擬內存。


最後,附上VB 調用的API 代碼

Option Explicit
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


SetProcessWorkingSetSize GetCurrentProcess, -1, -1

將當前進程使用的內存歸0,請放在適當的地方

二 區分物理內存、虛擬內存、Working Set(Memory)、Memory

以下來自:http://blog.joycode.com/qqchen/archive/2004/03/17/16434.aspx

這個問題在CSDN上碰到好幾次,我每次都只給出了簡單的答案:不要參考Task Manager的Mem Usage數據,那個數據的大小對程序性能沒有直接影響。
下面是我分析這問題的一些思路,希望對對這個問題感興趣的朋友有所幫助

Q: Is .NET Alone?
A:
 Nope! 前面Saucer說過了,這不是.NET的問題,所有Windows程序都有類似的行爲。例如下面的C程序:
void main { while(1); }   //死循環,便於我們察看Task Manager
初次運行在我的機器上Mem Usage是632K,把Console最小化以後再恢復,Mem Usage變成了36K。顯然,這不是一個.NET獨有的問題,而是Windows Memory Management的問題。那麼和.NET的GC機制也不會有太大的關係——雖然問題的表現形式很容易讓人聯想到GC。

Q: How much memory does my program use?
A:
 回答這個問題並不容易。先來看看操作系統虛擬內存管理的一些基本概念:每個Windows進程都擁有4G的地址空間,但是你的機器顯然沒有4G的物理內 存。在多任務環境下,所有進程使用的內存總和可以超過計算機的物理內存。在特定的情況下,進程的一部分可能會從物理內存中刪除而被暫存在硬盤的文件裏 (pagefile),當進程試圖訪問這些被交換到pagefile裏的內存的時候,系統會產生一個缺頁中斷(page fault),這時候Windows內存管理器會負責把對應的內存頁重新從硬盤調入物理內存。
在某個時間內,一個進程可以直接訪問到的物理內存(不發生缺頁中斷)叫做這個進程的Working Set;而一個進程從4G的地址空間當中實際分配(commit)了的、可訪問的內存稱爲Committed Virtual Memory。Committed VM可能存在於Page File當中,WorkingSet則一定位於物理內存。
所以要回答上面的問題先要反問一句:What're you talking about? Physical Memory or Committed Memory?

Q: What is this "Mem Usage" data?
A:
 From Task Manager Help: In Task Manager, the current Working Set of a process, in kilobytes. 
Mem Usage這個名字多少有些誤導。它只表示這個進程當前佔用的物理內存,也就是WorkingSet。WorkingSet不表示進程當前“佔用”的所有 虛擬內存,該進程可能還有一部分數據被交換到pagefile當中。這些數據只有在被訪問的時候纔會被加載到物理內存。
Task Manager有另一列數據:VM Size,表示了一個進程分配的虛存(Committed Visual Memory)——實際的定義要比這個複雜一些,但這個定義對我們目前分析的問題已經足夠了。以前面的C程序爲例,在最小化前後的VM Size都是176K,並沒有變化。
所以,結論很簡單:當一個Windows程序被最小化的時候,Windows內存管理器把該進程的WorkingSet減到最小(根據先進先出FIFO或者最近最少使用LRU),把大部分數據交換到pagefile裏。這很容易理解:我們通常總是希望爲前臺的應用程序留出更多物理內存,從而具有更好的性能。當該程序從最小化恢復的時候,Windows也不會完全加載程序的所有虛存,只是加載了必要的部分。這也很容易理解:程序啓動階段的代碼通常在啓動之後很少訪問(對.NET程序尤其如此,向fusion這樣的模塊在程序正常加載之後如果沒有用到Reflection通常用不到)。

Q: So, Do we want a smaller workingset, or a larger one?
A:
 It depends. Conventional Wisdom tells us: The smaller, the better. 但是在虛存的問題上卻沒這麼簡單。如果WorkingSet太小,程序運行過程中會產生很多缺頁中斷,這會嚴重影響程序的性能。另一方 面,WorkingSet太大會浪費“寶貴的”物理內存,降低整個系統的性能。 通常情況下(除非是對性能非常敏感的應用程序,並且你對Windows的內存管理瞭如指掌),建議不要在程序中自己調整WorkingSet的大小,而把 這個任務交給Windows內存管理器。調整的方法Saucer有提到: SetProcessWorkingSetSize();

Q: Final Question, Does my program really occupy that much physical memory?
A:
 這個問題看上去土了點——那個數字明明白白的寫在Task Manager裏面。
sam1111用 vadump檢查的結果顯示進程WorkingSet減小的主要原因是很多DLL在從最小化恢復的時候沒有被加載到物理內存。我們知道DLL的一個特點是 代碼共享,以NTDLL.DLL爲例,整個Windows系統的幾乎所有應用程序(具體地說,Win32子系統的所有程序)都需要引用 NTDLL.DLL,如果每人一份,光這個文件就的佔用幾十兆內存。Windows地解決辦法是隻在物理內存中保存一份NTDLL.DLL的COPY,所 有引用這個DLL的程序都把這一份COPY映射到自己的內存空間裏面,共享NTDLL.DLL的代碼段(每個進程的數據段仍然是獨立的)。所以雖然 NTDLL.DLL的大小被計算在你的程序的WorkingSet裏面,但是從你的程序中去掉對這個DLL的引用並不會真的釋放多少物理內存——你不用, 別人還在用呢!
所以,你的程序“獨佔”的物理內存遠沒有Mem Usage所表示的那麼多,需要從Mem Usage裏面扣除很多Shared Code Page (vadump裏面可以看到)。

結論?不要參考Task Manager的Mem Usage數據,那個數據的大小對程序性能沒有直接影響。用Perfomence Monitor裏面與.NET相關的Counter要容易、準確的多。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章