*前記:這幾天在最佳化系統的過程中,發現整個軟體剛一開機就佔了快200M的實體記憶體,在hp的工作站上面感覺都有點吃力,更別說在普通的PC上了。但是我發現軟體再最小化之後實體記憶體的佔用量只有10M左右,於是感覺整個軟體還有最佳化的空間,上網搜了一下搜到了一些解決辦法,不過感覺下面這篇更專業,於是就轉載了,以備不時之需。
在項目中對程式效能最佳化時,發現用SetProcessWorkingSetSize() 方法使記憶體降低了很多,於是查閱了相關的資料如下。
一 SetProcessWorkingSetSize 的工作原理
以下來自:
那麼我的程式為什麼能夠將佔用的記憶體移至虛擬記憶體呢?
其實,你也可以,試試看把一個程式最小化到工作列,再看看工作管理員,看到沒,你的程式佔用的實際記憶體一下子減少 了,看來並不是我有什麼方法能夠壓縮記憶體,而是作業系統本身就有這個機制,即當程式不使用時(最小化),作業系統會調用某些命令,來將該程式佔用的記憶體移 至虛擬記憶體,只保留一小部分常規代碼
所以我們就看到了 這種情景,佔用的記憶體一下子就縮小了。
那麼:系統到底調用了什麼指令呢?能不能在不縮小表單的情況下來釋放記憶體呢?
看看這個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.
使用這個函數來設定應用程式最小和最大的Runspace,只會保留需要的記憶體。當應用程式被閑置或系統記憶體太低時,作業系統會自動調用這個機制來設定應用程式的記憶體。應用程式也可以使用 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.
當你加大Runspace給應用程式,你能夠得到的實體記憶體取決於系統,這會造成其他應用程式降低效能或系統總體降低效能,這也可能導致請求實體記憶體的操作失敗,例如:建立 進程,線程,核心池,就必須小心的使用該函數。
========================
事實上,使用該函數並不能提高什麼效能,也不會真的節省記憶體。
因為他只是暫時的將應用程式佔用的記憶體移至虛擬記憶體,一旦,應用程式被啟用或者有操作請求時,這些記憶體又會被重新佔用。如果你強制使用該方法來 設定程式佔用的記憶體,那麼可能在一定程度上反而會降低系統效能,因為系統需要頻繁的進行記憶體和硬碟間的頁面交換。
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
以下來自:
這個問題在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有提到: ();
Q: Final Question, Does my program really occupy that much physical memory?
A: 這個問題看上去土了點——那個數字明明白白的寫在Task Manager裡面。
用 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要容易、準確的多。