国产一级a片免费看高清,亚洲熟女中文字幕在线视频,黄三级高清在线播放,免费黄色视频在线看

打開APP
userphoto
未登錄

開通VIP,暢享免費電子書等14項超值服

開通VIP
進程的虛擬地址空間 - so true - BlogJava
進程的虛擬地址空間
昨晚看到了深夜,終于對進程的虛擬地址空間有了個大致的了解,很激動,也很欣慰。回頭想來,一個程序員,真的應該知道這些知識,否則還真不太稱職。
首先告訴大家,我后面提到的這些知識在《windows核心編程》中都有,強烈建議大家把這本書翻翻,我相信會對你的編程境界拔高好幾個層次的??墒俏易罱鼪]那么多時間,因此就只能了解個大概,然后等今后閑暇時再看這本書吧。
昨天我媳婦還反復和我說:學東西必須要有選擇,不能對IT行業(yè)的所有知識亂學習,而且不要學那種實際意義不大的知識或是容易被淘汰的知識。其實她說的蠻對的,但是我要說,有關《windows核心編程》里的知識永遠都不會過時,因為它侵入到底層和內部了,就像C++,你覺得會過時嗎?就像windows永遠不會被淘汰一樣,呵呵。
下面我就來粗略的說說我了解的一些基本知識:
32位機器,每個程序有4G的虛擬地址空間。大致分為4塊,從低地址到高地址依次是:NULL區(qū),用戶區(qū),隔離區(qū),核心區(qū)。用戶私有的數據都在用戶區(qū)(當然這個區(qū)里又可以細分,其中也包括一部分可以共享的內容),系統(tǒng)內核等東西都在核心區(qū)??傮w來說,A進程的虛擬地址空間中的內容和B進程相比,只有各自的用戶區(qū)不一致。通常用戶區(qū)中,進程又會將exe文件(由頭數據和段數據組成)中定義的代碼段、堆棧段、數據段等各個段映射到用戶區(qū)的特定不同部位。對于這部分區(qū)域,用戶需要用VirtualAlloc先為自己預留后再提交,最后在自己的頁面被cpu訪問時再從exe映像中將數據加載到主存,然后將虛擬地址映射為主存的物理地址?;旧线@樣就可以了,至于系統(tǒng)如何進行頁面的管理以及地址映射如何實現等細節(jié)請大家再參考別的文獻。
我本以為很復雜呢,結果寫出來,就這么一小段,呵呵,看來是高估了自己理解的東西了,呵呵。
下面貼出我看的一些資料:
虛擬存儲器是一個抽象概念,它為每一個進程提供了一個假象,好像每個進程都在獨占的使用主存。每個進程看到的存儲器都是一致的,稱之為虛擬地址空間。
每個進程看到得虛擬地址空間有大量準確定義的區(qū)(area)構成,每個區(qū)都有專門的功能。從最低的地址看起:
程序代碼和數據:代碼是從同一固定地址開始,緊接著的是和C全局變量相對應的數據區(qū)。 (應該就是所謂的靜態(tài)存儲空間)
堆:代碼和數據區(qū)后緊隨著的是運行時堆。作為調用malloc和free這樣的C標準庫函數,堆可以在運行時動態(tài)的擴展和收縮。(應該就是所謂的動態(tài)存儲區(qū))
共享庫:在地址空間的中間附近是一塊用來存放像C標準庫和數學庫這樣共享庫的代碼和數據的區(qū)域。(C標準庫函數的指令,連接階段把他們加入到編譯后的程序)
棧:位于用戶虛擬地址空間頂部的是用戶棧,編譯器用它來實現函數調用。和堆一樣每次我們從函數返回時,棧就會收縮。
內核虛擬存儲器:內核是操作系統(tǒng)總是駐留在存儲器中的部分。地址空間頂部的四分之一部分是為內核預留的。(系統(tǒng)函數?這里說的UNIX系統(tǒng),不知道windows下是不是這樣的?)
今天大多數計算機的字長都是32字節(jié),這就限制了虛擬地址空間為4千兆字節(jié)(4GB)
引言
Windows的內存結構是深入理解Windows操作系統(tǒng)如何運作的最關鍵之所在,通過對內存結構的認識可清楚地了解諸如進程間數據的共享、對內存進行有效的管理等問題,從而能夠在程序設計時使程序以更加有效的方式運行。Windows操作系統(tǒng)對內存的管理可采取多種不同的方式,其中虛擬內存的管理方式可用來管理大型的對象和結構數組。
在Windows系統(tǒng)中,任何一個進程都被賦予其自己的虛擬地址空間,該虛擬地址空間覆蓋了一個相當大的范圍,對于32位進程,其地址空間為232=4,294,967,296 Byte,這使得一個指針可以使用從0x00000000到0xFFFFFFFF的4GB范圍之內的任何一個值。雖然每一個32位進程可使用4GB的地址空間,但并不意味著每一個進程實際擁有4GB的物理地址空間,該地址空間僅僅是一個虛擬地址空間,此虛擬地址空間只是內存地址的一個范圍。進程實際可以得到的物理內存要遠小于其虛擬地址空間。進程的虛擬地址空間是為每個進程所私有的,在進程內運行的線程對內存空間的訪問都被限制在調用進程之內,而不能訪問屬于其他進程的內存空間。這樣,在不同的進程中可以使用相同地址的指針來指向屬于各自調用進程的內容而不會由此引起混亂。下面分別對虛擬內存的各具體技術進行介紹。
地址空間中區(qū)域的保留與釋放
在進程創(chuàng)建之初并被賦予地址空間時,其虛擬地址空間尚未分配,處于空閑狀態(tài)。這時地址空間內的內存是不能使用的,必須首先通過VirtualAlloc()函數來分配其內的各個區(qū)域,對其進行保留。
LPVOID VirtualAlloc(
LPVOID lpAddress,
DWORD dwSize,
DWORD flAllocationType,
DWORD flProtect
);
其參數lpAddress包含一個內存地址,用于定義待分配區(qū)域的首地址。通??蓪⒋藚翟O置為NULL,由系統(tǒng)通過搜索地址空間來決定滿足條件的未保留地址空間。這時系統(tǒng)可從地址空間的任意位置處開始保留一個區(qū)域,而且還可以通過向參數flAllocationType設置MEM_TOP_DOWN標志來指明在盡可能高的地址上分配內存。如果不希望由系統(tǒng)自動完成對內存區(qū)域的分配而為lpAddress設定了內存地址(必須確保其始終位于進程的用戶模式分區(qū)中,否則將會導致分配的失?。?,那么系統(tǒng)將在進行分配之前首先檢查在該內存地址上是否存在足夠大的未保留空間,如果存在一個足夠大的空閑區(qū)域,那么系統(tǒng)將會保留此區(qū)域并返回此保留區(qū)域的虛擬地址,否則將導致分配的失敗而返回NULL。這里需要特別指出的是,在指定lpAddress的內存地址時,必須確保是從一個分配粒度的邊界處開始。
一般來說,在不同的CPU平臺下分配粒度各不相同,但目前所有Windows環(huán)境下的CPU如x86、32位Alpha、64位Alpha以及IA-64等均是采用64KB的分配粒度。如果保留區(qū)域的起始地址沒有遵循從64KB分配粒度的邊界開始之一原則,系統(tǒng)將自動調整該地址到最接近的64K的倍數。例如,如果指定的lpAddress為0x00781022,那么此保留區(qū)域實際是從0x00780000開始分配的。參數dwSize指定了保留區(qū)域的大小。但是系統(tǒng)實際保留的區(qū)域大小必須是CPU頁面大小的整數倍,如果指定的dwSize并非CPU頁面的整數倍,系統(tǒng)將自動對其進行調整,使其達到與之最接近的頁面大小整數倍。與分配粒度一樣,對于不同的CPU平臺其頁面大小也是不一樣的。在x86平臺下,頁面大小為4KB,在32位Alpah平臺下,頁面大小為8KB。在使用時可以通過GetSystemInfo()來決定當前主機的頁面大小。參數flAllocationType和flProtect分別定義了分配類型和訪問保護屬性。由于VirtualAlloc()可用來保留一個區(qū)域也可以用來占用物理存儲器,因此通過flAllocationType來指定當前是要保留一個區(qū)域還是要占用物理存儲器。其可能使用的內存分配類型有:
分配類型
類型說明
MEM_COMMIT
為特定的頁面區(qū)域分配內存中或磁盤的頁面文件中的物理存儲
MEM_PHYSICAL
分配物理內存(僅用于地址窗口擴展內存)
MEM_RESERVE
保留進程的虛擬地址空間,而不分配任何物理存儲。保留頁面可通過繼續(xù)調用VirtualAlloc()而被占用
MEM_RESET
指明在內存中由參數lpAddress和dwSize指定的數據無效
MEM_TOP_DOWN
在盡可能高的地址上分配內存(Windows 98忽略此標志)
MEM_WRITE_WATCH
必須與MEM_RESERVE一起指定,使系統(tǒng)跟蹤那些被寫入分配區(qū)域的頁面(僅針對Windows 98)
分配成功完成后,即在進程的虛擬地址空間中保留了一個區(qū)域,可以對此區(qū)域中的內存進行保護權限許可范圍內的訪問。當不再需要訪問此地址空間區(qū)域時,應釋放此區(qū)域。由VirtualFree()負責完成。其函數原型為:
BOOL VirtualFree(
LPVOID lpAddress,
DWORD dwSize,
DWORD dwFreeType
);
其中,參數lpAddress為指向待釋放頁面區(qū)域的指針。如果參數dwFreeType指定了MEM_RELEASE,則lpAddress必須為頁面區(qū)域被保留時由VirtualAlloc()所返回的基地址。參數dwSize指定了要釋放的地址空間區(qū)域的大小,如果參數dwFreeType指定了MEM_RELEASE標志,則將dwSize設置為0,由系統(tǒng)計算在特定內存地址上的待釋放區(qū)域的大小。參數dwFreeType為所執(zhí)行的釋放操作的類型,其可能的取值為MEM_RELEASE和MEM_DECOMMIT,其中MEM_RELEASE標志指明要釋放指定的保留頁面區(qū)域,MEM_DECOMMIT標志則對指定的占用頁面區(qū)域進行占用的解除。如果VirtualFree()成功執(zhí)行完成,將回收全部范圍的已分配頁面,此后如再對這些已釋放頁面區(qū)域內存的訪問將引發(fā)內存訪問異常。釋放后的頁面區(qū)域可供系統(tǒng)繼續(xù)分配使用。
下面這段代碼演示了由系統(tǒng)在進程的用戶模式分區(qū)內保留一個64KB大小的區(qū)域,并將其釋放的過程:
// 在地址空間中保留一個區(qū)域
LPBYTE bBuffer = (LPBYTE)VirtualAlloc(NULL, 65536, MEM_RESERVE, PAGE_READWRITE);
……
// 釋放已保留的區(qū)域
VirtualFree(bBuffer, 0, MEM_RELEASE);
flProtect頁面保護屬性
我們可以給每個已分配的物理存儲頁指定不同的頁面保護屬性。表13-3列出了所有的頁面保護屬性。
表13-3  內存頁面保護屬性
保護屬性
描  述
PAGE_NOACCESS
試圖讀取頁面、寫入頁面或執(zhí)行頁面中的代碼將引發(fā)訪問違規(guī)
PAGE_READONLY
試圖寫入頁面或執(zhí)行頁面中的代碼將引發(fā)訪問違規(guī)
PAGE_READWRITE
試圖執(zhí)行頁面中的代碼將引發(fā)訪問違規(guī)
PAGE_EXECUTE
試圖讀取頁面或寫入頁面將引發(fā)訪問違規(guī)
PAGE_EXECUTE_READ
試圖寫入頁面將引發(fā)訪問違規(guī)
PAGE_EXECUTE_READWRITE
對頁面執(zhí)行任何操作都不會引發(fā)訪問違規(guī)
PAGE_WRITECOPY
試圖執(zhí)行頁面中的代碼將引發(fā)訪問違規(guī)。試圖寫入頁面將使系統(tǒng)為進程單獨創(chuàng)建一份該頁面的私有副本(以頁交換文件為后備存儲器)
PAGE_EXECUTE_WRITECOPY
對頁面執(zhí)行任何操作都不會引發(fā)訪問違規(guī)。試圖寫入頁面將使系統(tǒng)為進程單獨創(chuàng)建一份該頁面的私有副本(以頁交換文件為后備存儲器)
一些惡意軟件將代碼寫入到用于數據的內存區(qū)域(比如線程棧上),通過這種方式讓應用程序執(zhí)行惡意代碼。Windows的數據執(zhí)行保護(Data Execution Protection,后面簡稱為DEP)特性提供了對此類惡意攻擊的防護。如果啟用了DEP,那么只有對那些真正需要執(zhí)行代碼的內存區(qū)域,操作系統(tǒng)才會使用PAGE_EXECUTE_*保護屬性。其他保護屬性(最常見的就是PAGE_READWRITE)用于只應該存放數據的內存區(qū)域(比如線程棧和應用程序的堆)。
如果CPU試圖執(zhí)行某個頁面中的代碼,而該頁又沒有PAGE_EXECUTE_*保護屬性,那么CPU會拋出訪問違規(guī)異常。
系統(tǒng)還對Windows支持的結構化異常處理機制(structured exception handling mechanism)做了更進一步的保護,結構化異常處理機制會在第23~25章詳細介紹。如果應用程序在鏈接時使用了/SAFESEH開關,那么異常處理器會被注冊到映像文件中一個特殊的表中。這樣,當將要執(zhí)行一個異常處理器時,操作系統(tǒng)會先檢查該處理器有沒有在表中注冊過,然后決定是否允許它執(zhí)行。
有關DEP的更多信息,請訪問http://go.microsoft.com/fwlink/?LinkId=28022,可以在此找到Microsoft白皮書“03_CIF_Memory_Protection.DOC”。
13.6.1  寫時復制
在表13.3中列出的保護屬性中,除最后兩個屬性PAGE_WRITECOPY和PAGE_EXECUTE_WRITECOPY之外,其余的都不言自明。這兩個保護屬性存在的目的是為了節(jié)省內存和頁交換文件的使用。Windows支持一種機制,允許兩個或兩個以上的進程共享同一塊存儲器。因此,如果有10個記事本程序正在運行,所有的進程會共享應用程序的代碼頁和數據頁。讓所有的應用程序實例共享相同的存儲頁極大地提升了系統(tǒng)的性能,但另一方面,這也要求所有的應用程序實例只能讀取其中的數據或是執(zhí)行其中的代碼。如果某個應用程序實例修改并寫入一個存儲頁,那么這等于是修改了其他實例正在使用的存儲頁,最終將導致混亂。
為了避免此類混亂的發(fā)生,操作系統(tǒng)會給共享的存儲頁指定寫時復制屬性。當系統(tǒng)把一個.exe或.dll映射到一個地址空間的時候,系統(tǒng)會計算有多少頁面是可寫的。(通常,包含代碼的頁面被標記為PAGE_EXECUTE_READ,而包含數據的頁面被標記為PAGE_READWRITE。)然后系統(tǒng)會從頁交換文件中分配存儲空間來容納這些可寫頁面。除非應用程序真的寫入可寫頁面,否則不會用到頁交換文件中的存儲器。
當線程試圖寫入一個共享頁面時,系統(tǒng)會介入并執(zhí)行下面的操作。
(1)   系統(tǒng)在內存中找到一個閑置頁面。注意,該閑置頁面的后備頁面來自頁交換文件,它是系統(tǒng)最初將模塊映射到進程的地址空間時分配的。由于系統(tǒng)在第一次進行映射的時候分配了所有可能需要的頁交換文件空間,這一步不可能失敗。
(2)   系統(tǒng)把線程想要修改的頁面內容復制到在第1步中找到的閑置頁面。系統(tǒng)會給該閑置頁面指定PAGE_READWRITE或PAGE_EXECUTE_READWRITE保護屬性,系統(tǒng)不會對原始頁面的保護屬性和數據做任何修改。
(3)   然后,系統(tǒng)更新進程的頁面表,這樣一來,原來的虛擬地址現在就對應到內存中一個新的頁面了。
系統(tǒng)在執(zhí)行這些步驟之后,進程就可以訪問它自己的副本了。第17章將進一步介紹存儲器共享和寫時復制。
此外,在預訂地址空間或調撥物理存儲器時,不能使用PAGE_WRITECOPY或PAGE_EXECUTE_WRITECOPY保護屬性。這樣做會導致調用VirtualAlloc失敗,此時調用GetLastError會返回錯誤碼ERROR_INVALID_PARAMETER。這兩個屬性是操作系統(tǒng)在映射.exe或DLL映像文件時用的。
13.6.2  一些特殊的訪問保護屬性標志
除了已經介紹過的保護屬性之外,另外還有3個保護屬性標志:PAGE_NOCACHE,PAGE_WRITECOMBINE和PAGE_GUARD。使用這些標志時,只需將它們與除了PAGE_NOACCESS之外的任何其他保護屬性進行按位或操作即可。
第一個保護屬性標志PAGE_NOCACHE,用來禁止對已調撥的頁面進行緩存。該標志存在的主要目的是為了讓需要操控內存緩沖區(qū)的驅動程序開發(fā)人員使用,不建議將該標志用于除此以外的其他用途。
第二個保護屬性標志PAGE_WRITECOMBINE也是給驅動程序開發(fā)人員用的。它允許把對單個設備的多次寫操作組合在一起,以提高性能。
最后一個保護屬性標志PAGE_GUARD,使應用程序能夠在頁面中的任何一個字節(jié)被寫入時得到通知。這個標志有一些巧妙的用法。Windows在創(chuàng)建線程棧時會用到它。有關該標志的更多信息,請參閱第16章。
物理存儲器的提交與回收
在地址空間中保留一個區(qū)域后,并不能直接對其進行使用,必須在把物理存儲器提交給該區(qū)域后,才可以訪問區(qū)域中的內存地址。在提交過程中,物理存儲器是按頁面邊界和頁面大小的塊來進行提交的。若要為一個已保留的地址空間區(qū)域提交物理存儲器,需要再次調用VirtualAlloc()函數,所不同的是在執(zhí)行物理存儲器的提交過程中需要指定flAllocationType參數為MEM_COMMIT標志,使用的保護屬性與保留區(qū)域時所用保護屬性一致。在提交時,可以將物理存儲器提交給整個保留區(qū)域,也可以進行部分提交,由VirtualAlloc()函數的lpAddress參數和dwSize參數指明要將物理存儲器提交到何處以及要提交多少物理存儲器。
與保留區(qū)域的釋放類似,當不再需要訪問保留區(qū)域中被提交的物理存儲器時,提交的物理存儲器應得到及時的釋放。該回收過程與保留區(qū)域的釋放一樣也是通過VirtualFree()函數來完成的。在調用時為VirtualFree()的dwFreeType參數指定MEM_DECOMMIT標志,并在參數lpAddress和dwSize中傳遞用來標識要解除的第一個頁面的內存地址和要釋放的字節(jié)數。此回收過程同樣也是以頁面為單位來進行的,將回收設定范圍所涉及到的所有頁面。下面這段代碼演示了對先前保留區(qū)域的提交過程,并在使用完畢后將其回收:
// 在地址空間中保留一個區(qū)域
LPBYTE bBuffer = (LPBYTE)VirtualAlloc(NULL, 65536, MEM_RESERVE, PAGE_READWRITE);
// 提交物理存儲器
VirtualAlloc(bBuffer, 65536, MEM_COMMIT, PAGE_READWRITE);
……
// 回收提交的物理存儲器
VirtualFree(bBuffer, 65536, MEM_DECOMMIT);
// 釋放已保留的區(qū)域
VirtualFree(bBuffer, 0, MEM_RELEASE);
由于未經提交的保留區(qū)域實際是無法使用的,因此在編程過程中允許通過一次VirtualAlloc()調用而完成對地址空間的區(qū)域保留及對保留區(qū)域的物理存儲器的提交。相應的,回收、釋放過程也可由一次VirtualFree()調用來實現。上述代碼可按此方法改寫為:
// 在地址空間中保留一個區(qū)域并提交物理存儲器
LPBYTE bBuffer = (LPBYTE)VirtualAlloc(NULL, 65536, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
……
// 釋放已保留的區(qū)域并回收提交的物理存儲器
VirtualFree(bBuffer, 0, MEM_RELEASE | MEM_DECOMMIT);
頁文件的使用
在前面曾多次提到物理存儲器,這里所說的物理存儲器并不局限于計算機內存,還包括在磁盤空間上創(chuàng)建的頁文件,其存儲空間大小為計算機內存和頁文件存儲容量之和。由于通常情況下磁盤存儲空間要遠大于內存的存儲空間,因此頁文件的使用對于應用程序而言相當于透明的增加了其所能使用的內存容量。在使用時,由操作系統(tǒng)和CPU負責對頁文件進行維護和協(xié)調。只有在應用程序需要時才臨時將頁文件中的數據加載到內存供應用程序訪問之用,在使用完畢后再從內存交換回頁文件。
進程中的線程在訪問位于已提交物理存儲器的保留區(qū)域的內存地址時,如果此地址指向的數據當前已存在于內存,CPU將直接將進程的虛擬地址映射為物理地址,并完成對數據的訪問;如果此數據是存在于頁文件中的,就要試圖將此數據從頁文件加載到內存。在進行此處理時,首先要檢查內存中是否有可供使用的空閑頁面,如果有就可以直接將數據加載到內存中的空閑頁面,否則就要從內存中尋找一個暫不使用的可釋放的頁面并將數據加載到此頁面。如果被釋放頁面中的數據仍為有效數據(即以后還會用到),就要先將此頁面從內存寫入到頁文件。在數據加載到內存后,仍要在CPU將虛擬地址映射為物理地址后方可實現對數據的訪問。與對物理存儲器中數據的訪問有所不同,在運行可執(zhí)行程序時并不進行程序代碼和數據的從磁盤文件到頁文件的復制過程,而是在確定了程序的代碼及其數據的大小后,由系統(tǒng)直接將可執(zhí)行程序的映像用作程序的保留地址空間區(qū)域。這樣的處理方式大大縮短了程序的啟動時間,并可減小頁文件的尺寸。
上面提到的“數據是否在內存中”,我認為應該是判斷系統(tǒng)緩存中是否有需要的頁面。
==========================================================================================
對內存的管理
使用虛擬內存技術將能夠對內存進行管理。對當前內存狀態(tài)的動態(tài)信息可通過GlobalMemoryStatus()函數來獲取。GlobalMemoryStatus()的函數原型為:
VOID GlobalMemoryStatus(LPMEMORYSTATUS lpBuffer);
其參數lpBuffer為一個指向內存狀態(tài)結構MEMORYSTATUS的指針,而且要預先對該結構對象的數據成員進行初始化。MEMORYSTATUS結構定義如下:
typedef struct _MEMORYSTATUS {
DWORD dwLength; // MEMORYSTATUS結構大小
DWORD dwMemoryLoad; // 已使用內存所占的百分比
DWORD dwTotalPhys; // 物理存儲器的總字節(jié)數
DWORD dwAvailPhys; // 空閑物理存儲器的字節(jié)數
DWORD dwTotalPageFile; // 頁文件包含的最大字節(jié)數
DWORD dwAvailPageFile; // 頁文件可用字節(jié)數
DWORD dwTotalVirtual; // 用戶模式分區(qū)大小
DWORD dwAvailVirtual; // 用戶模式分區(qū)中空閑內存大小
} MEMORYSTATUS, *LPMEMORYSTATUS;
下面這段代碼通過設置一個定時器而每隔5秒更新一次當前系統(tǒng)對內存的使用情況:
// 設置定時器
SetTimer(0, 5000, NULL);
……
void CSample22Dlg::OnTimer(UINT nIDEvent)
{
// 獲取當前內存使用狀態(tài)
MEMORYSTATUS mst;
GlobalMemoryStatus(&mst);
// 已使用內存所占的百分比
m_dwMemoryLoad = mst.dwMemoryLoad;
// 物理存儲器的總字節(jié)數
m_dwAvailPhys = mst.dwAvailPhys / 1024;
// 空閑物理存儲器的字節(jié)數
m_dwAvailPageFile = mst.dwAvailPageFile / 1024;
// 頁文件包含的最大字節(jié)數
m_dwAvailVirtual = mst.dwAvailVirtual / 1024;
// 頁文件可用字節(jié)數
m_dwTotalPageFile = mst.dwTotalPageFile / 1024;
// 用戶模式分區(qū)大小
m_dwTotalPhys = mst.dwTotalPhys / 1024;
// 用戶模式分區(qū)中空閑內存大小
m_dwTotalVirtual = mst.dwTotalVirtual / 1024;
// 更新顯示
UpdateData(FALSE);
CDialog::OnTimer(nIDEvent);
}
對內存的管理除了對當前內存的使用狀態(tài)信息進行獲取外,還經常需要獲取有關進程的虛擬地址空間的狀態(tài)信息。可由VirtualQuery()函數來進行查詢,其原型聲明如下:
DWORD VirtualQuery(
LPCVOID lpAddress, // 內存地址
PMEMORY_BASIC_INFORMATION lpBuffer, // 指向內存信息結構的指針
DWORD dwLength // 內存的大小
);
其中l(wèi)pAddress參數為要查詢的虛擬內存地址,該值將被調整到最近的頁邊界處。當前計算機的頁面大小可通過GetSystemInfo()函數獲取,該函數需要一個指向SYSTEM_INFO結構的指針作為參數,獲取到的系統(tǒng)信息將填充在該數據結構對象中。下面這段代碼通過對GetSystemInfo()的調用而獲取了當前的系統(tǒng)信息:
// 得到當前系統(tǒng)信息
GetSystemInfo(&m_sin);
// 位屏蔽,指明哪個CPU是活動的
m_dwActiveProcessorMask = m_sin.dwActiveProcessorMask;
// 保留的地址空間區(qū)域的分配粒度
m_dwAllocationGranularity = m_sin.dwAllocationGranularity;
// 進程的可用地址空間的最小內存地址
m_dwMaxApplicationAddress = (DWORD)m_sin.lpMaximumApplicationAddress;
// 進程的可用地址空間的最大內存地址
m_dwMinApplicationAddress = (DWORD)m_sin.lpMinimumApplicationAddress;
// 計算機中CPU的數目
m_dwNumberOfProcessors = m_sin.dwNumberOfProcessors;
// 頁面大小
m_dwPageSize = m_sin.dwPageSize;
// 處理器類型
m_dwProcessorType = m_sin.dwProcessorType;
//進一步細分處理器級別
m_wProcessorLevel = m_sin.wProcessorLevel;
// 系統(tǒng)處理器的結構
m_wProcessorArchitecture = m_sin.wProcessorArchitecture;
// 更新顯示
UpdateData(FALSE);
VirtualQuery()的第二個參數lpBuffer為一個指向MEMORY_BASIC_INFORMATION結構的指針。VirtualQuery()如成功執(zhí)行,該結構對象中將保存查詢到的虛擬地址空間狀態(tài)信息。MEMORY_BASIC_INFORMATION結構的定義為:
typedef struct _MEMORY_BASIC_INFORMATION {
PVOID BaseAddress; // 保留區(qū)域的基地址
PVOID AllocationBase; // 分配的基地址
DWORD AllocationProtect; // 初次保留時所設置的保護屬性
DWORD RegionSize; // 區(qū)域大小
DWORD State; // 狀態(tài)(提交、保留或空閑)
DWORD Protect; // 當前訪問保護屬性
DWORD Type; // 頁面類型
} MEMORY_BASIC_INFORMATION;
通過VirtualQuery()函數對由lpAddress和dwLength參數指定的虛擬地址空間區(qū)域的查詢而獲取得到的相關狀態(tài)信息:
// 更新顯示
UpdateData(TRUE);
// 虛擬地址空間狀態(tài)結構
MEMORY_BASIC_INFORMATION mbi;
// 查詢指定虛擬地址空間的狀態(tài)信息
VirtualQuery((LPCVOID)m_dwAddress, &mbi, 1024);
// 保留區(qū)域的基地址
m_dwBaseAddress = (DWORD)mbi.BaseAddress;
// 分配的基地址
m_dwAllocateBase = (DWORD)mbi.AllocationBase;
// 初次保留時所設置的保護屬性
m_dwAllocateProtect = mbi.AllocationProtect;
// 區(qū)域大小
m_dwRegionSize = mbi.RegionSize;
// 狀態(tài)(提交、保留或空閑)
m_dwState = mbi.State;
// 當前訪問保護屬性
m_dwProtect = mbi.Protect;
// 頁面類型
m_dwType = mbi.Type;
// 更新顯示
UpdateData(FALSE);
小結
本文主要對內存管理中的虛擬內存技術的基本原理、使用方法和對內存的管理等進行了介紹。通過本文將能夠掌握虛擬內存的一般使用方法,與之相關的內存管理技術還包括內存文件映射和堆管理等技術,讀者可參閱相關文章。這幾種內存管理技術同屬Windows編程中的高級技術,在應用程序中適當使用將有助于程序性能的提高。本文所述程序在Windows 2000 Professional下由Microsoft Viusual C++ 6.0編譯通過。
進程的虛擬地址空間
每個進程都被賦予它自己的虛擬地址空間。對于3 2位進程來說,這個地址空間是4 G B,因為3 2位指針可以擁有從0 x 0 0 0 0 0 0 0 0至0 x F F F F F F F F之間的任何一個值。這使得一個指針能夠擁有4 294 967 296個值中的一個值,它覆蓋了一個進程的4 G B虛擬空間的范圍。對于6 4位進程來說,這個地址空間是1 6 E B(1 01 8字節(jié)),因為6 4位指針可以擁有從0 x 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0至0 x F F F F F F F F F F F F F F F F之間的任何值。這使得一個指針可以擁有18 446 744 073 709 551 616個值中的一個值,它覆蓋了一個進程的1 6 E B虛擬空間的范圍。這是相當大的一個范圍。
由于每個進程可以接收它自己的私有的地址空間,因此當進程中的一個線程正在運行時,該線程可以訪問只屬于它的進程的內存。屬于所有其他進程的內存則隱藏著,并且不能被正在運行的線程訪問。
注意在Windows 2000中,屬于操作系統(tǒng)本身的內存也是隱藏的,正在運行的線程無法訪問。這意味著線程常常不能訪問操作系統(tǒng)的數據。Windows 98中,屬于操作系統(tǒng)的內存是不隱藏的,正在運行的線程可以訪問。因此,正在運行的線程常??梢栽L問操作系統(tǒng)的數據,也可以破壞操作系統(tǒng)(從而有可能導致操作系統(tǒng)崩潰)。在Windows 98中,一個進程的線程不可能訪問屬于另一個進程的內存。
前面說過,每個進程有它自己的私有地址空間。進程A可能有一個存放在它的地址空間中的數據結構,地址是0 x 1 2 3 4 5 6 7 8,而進程B則有一個完全不同的數據結構存放在它的地址空間中,地址是0 x 1 2 3 4 5 6 7 8。當進程A中運行的線程訪問地址為0 x 1 2 3 4 5 6 7 8的內存時,這些線程訪問的是進程A的數據結構。當進程B中運行的線程訪問地址為0 x 1 2 3 4 5 6 7 8的內存時,這些線程訪問的是進程B的數據結構。進程A中運行的線程不能訪問進程B的地址空間中的數據結構。反之亦然。
當你因為擁有如此大的地址空間可以用于應用程序而興高采烈之前,記住,這是個虛擬地址空間,不是物理地址空間。該地址空間只是內存地址的一個范圍。在你能夠成功地訪問數據而不會出現違規(guī)訪問之前,必須賦予物理存儲器,或者將物理存儲器映射到各個部分的地址空間。本章后面將要具體介紹這是如何操作的。
虛擬地址空間如何分區(qū)
每個進程的虛擬地址空間都要劃分成各個分區(qū)。地址空間的分區(qū)是根據操作系統(tǒng)的基本實現方法來進行的。不同的Wi n d o w s內核,其分區(qū)也略有不同。表 1顯示了每種平臺是如何對進程的地址空間進行分區(qū)的。
表1 進程的地址空間如何分區(qū)
分區(qū)
32位Windows 2000(x86和Alpha處理器)
32位Windows 2000(x86w/3GB用戶方式)
64位Windows 2000(Alpha和IA-64處理器)
Windows 98
N U L L指針分配的分區(qū)
0 x 0 0 0 0 0 0 0 0  ——0x 0 0 0 0 F F F F
0 x 0 0 0 0 0 0 0 0 0 x 0 0 0 0 F F F F
0x00000000 00000000 0x00000000 0000FFFF
0 x 0 0 0 0 0 0 0 0 0 x 0 0 0 0 0 F F F
DOS/16位Windows應用程序兼容分區(qū)
0 x 0 0 0 0 0 1 0 0 0 0 x 0 0 3 F F F F F
用戶方式
0 x 0 0 0 1 0 0 0 0—— 0 x 7 F F E F F F F<將近2G>
0 x 0 0 0 1 0 0 0 0 0 x B F F E F F F F F
0x00000000 00010000 0x000003FF FFFEFFFF
0 x 0 0 4 0 0 0 0 0 0 x 7 F F F F F F F
64-KB禁止進入分區(qū)
0 x 7 F F F 0 0 0 0——0x7FFF FFFF
0 x B F F F 0 0 0 0——0 x B F F F F F F F
0 x 0 0 0 0 0 3 F F F F F F 0 0 0 0——0 x 0 0 0 0 0 3 F F F F F F F F F F
共享內存映射
0 x 8 0 0 0 0 0 0 0
文件(MMF)內核方式
0 x 8 0 0 0 0 0 0 0 —— 0 x F F F F F F F F<共2G>
0 x C 0 0 0 0 0 0 0 0 x F F F F F F F F
0x00000400 00000000 0xFFFFFFFFF FFFFFFF
0 x B F F F F F F F 0 x C 0 0 0 0 0 0 0 0 x F F F F F F F F
1. NULL指針分區(qū)是NULL指針的地址范圍。
對這個區(qū)域的讀寫企圖都將引發(fā)訪問違規(guī)。
2. DOS/WIN16分區(qū)是98中專門用于16位的
DOS和windows程序運行的空間,所有的16
位程序將共享這個4M的空間。Win2000中不
存在這個分區(qū),16位程序也會擁有自己獨立的虛擬地址空間。有的文章中稱win2000中不能運行16位程序,是不確切的。
3.用戶分區(qū)是進程的私有領域,Win2000中,程序的可執(zhí)行代碼和其它用戶模塊均加載在這里,內存映射文件也會加載在這里。Win98中的系統(tǒng)共享DLL和內存映射文件則加載在共享分區(qū)中。
4.禁止訪問分區(qū)只有在win2000中有。這個分區(qū)是用戶分區(qū)和內核分區(qū)之間的一個隔離帶,目的是為了防止用戶程序違規(guī)訪問內核分區(qū)。
5. MMF分區(qū)只有win98中有,所有的內存映射文件和系統(tǒng)共享DLL將加載在這個地址。而2000中則將其加載到用戶分區(qū)。
6. 內核方式分區(qū)對用戶的程序來說是禁止訪問的,操作系統(tǒng)的代碼在此。內核對象也駐留在此。
另外要說明的是,win98中對于內核分區(qū)本也應該提供保護的,但遺憾的是并沒有做到,因而98中程序可以訪問內核分區(qū)的地址空間。
對于用戶分區(qū),又可以細分成若干區(qū)域。(這些區(qū)域具體會在第四階段詳細剖析。因為這部分內容牽扯到PE文件結構,只有學習并理解了PE文件結構后,才能理解這部分內容,為了便于后面的講解,在此講這部分區(qū)域先大致分為4塊:)
3 2位Windows 2000的內核與6 4位Windows 2000的內核擁有大體相同的分區(qū),差別在于分區(qū)的大小和位置有所不同。另一方面,可以看到Windows 98下的分區(qū)有著很大的不同。下面讓我們看一下系統(tǒng)是如何使用每一個分區(qū)的。
NULL指針分配的分區(qū)—適用于Windows 2000和Windows 98
進程地址空間的這個分區(qū)的設置是為了幫助程序員掌握N U L L指針的分配情況。如果你的進程中的線程試圖讀取該分區(qū)的地址空間的數據,或者將數據寫入該分區(qū)的地址空間,那么C P U就會引發(fā)一個訪問違規(guī)。保護這個分區(qū)是極其有用的,它可以幫助你發(fā)現N U L L指針的分配情況。
C / C + +程序中常常不進行嚴格的錯誤檢查。例如,下面這個代碼就沒有進行任何錯誤檢查:
int* pnSomeInteger = (int*) malloc(sizeof(int));*pnSomeInteger = 5;
如果m a l l o c不能找到足夠的內存來滿足需要,它就返回N U L L。但是,該代碼并不檢查這種可能性,它認為地址的分配已經取得成功,并且開始訪問0 x 0 0 0 0 0 0 0 0地址的內存。由于這個分區(qū)的地址空間是禁止進入的,因此就會發(fā)生內存訪問違規(guī)現象,同時該進程將終止運行。這個特性有助于編程員發(fā)現應用程序中的錯誤。
用戶方式分區(qū)—適用于Windows 2000和Windows 98
這個分區(qū)是進程的私有(非共享)地址空間所在的地方。一個進程不能讀取、寫入、或者以任何方式訪問駐留在該分區(qū)中的另一個進程的數據。對于所有應用程序來說,該分區(qū)是維護進程的大部分數據的地方。由于每個進程可以得到它自己的私有的、非共享分區(qū),以便存放它的數據,因此,應用程序不太可能被其他應用程序所破壞,這使得整個系統(tǒng)更加健壯。
在Windows 2000中,所有的. e x e和D L L模塊均加載這個分區(qū)。每個進程可以將這些D L L加載到該分區(qū)的不同地址中(不過這種可能性很?。O到y(tǒng)還可以在這個分區(qū)中映射該進程可以訪問的所有內存映射文件
共享的MMF分區(qū)—僅適用于Windows 98
這個1 G B分區(qū)是系統(tǒng)用來存放所有3 2位進程共享數據的地方。例如,系統(tǒng)的動態(tài)鏈接庫K e r n e l 3 2 . d l l、A d v A P I 3 2 . d l l、U s e r 3 2 . d l l和G D I 3 2 . d l l等,全部存放在這個地址空間分區(qū)中,因此,所有3 2位進程都能很容易同時訪問它們。系統(tǒng)還為每個進程將D L L加載相同的內存地址。此外,系統(tǒng)將所有內存映射文件映射到這個分區(qū)中。
物理存儲器與頁文件
在較老的操作系統(tǒng)中,物理存儲器被視為計算機擁有的R A M的容量。換句話說,如果計算機擁有1 6 M B的R A M,那么加載和運行的應用程序最多可以使用1 6 M B的R A M。今天的操作系統(tǒng)能夠使得磁盤空間看上去就像內存一樣。磁盤上的文件通常稱為頁文件,它包含了可供所有進程使用的虛擬內存
當然,若要使虛擬內存能夠運行,需要得到C P U本身的大量幫助。當一個線程試圖訪問一個字節(jié)的內存時, C P U必須知道這個字節(jié)是在R A M中還是在磁盤上。
從應用程序的角度來看,頁文件透明地增加了應用程序能夠使用的R A M(即內存)的數量。如果計算機擁有6 4 M B的R A M,同時在硬盤上有一個100 MB的頁文件,那么運行的應用程序就認為計算機總共擁有1 6 4 M B的R A M。
實際上并不擁有1 6 4 M B的R A M。相反,操作系統(tǒng)與C P U相協(xié)調,共同將R A M的各個部分保存到頁文件中,當運行的應用程序需要時,再將頁文件的各個部分重新加載到R A M。由于頁文件增加了應用程序可以使用的R A M的容量,因此頁文件的使用是視情況而定的。如果沒有頁文件,那么系統(tǒng)就認為只有較少的R A M可供應用程序使用。但是,我們鼓勵用戶使用頁文件,這樣他們就能夠運行更多的應用程序,并且這些應用程序能夠對更大的數據集進行操作。最好將物理存儲器視為存儲在磁盤驅動器(通常是硬盤驅動器)上的頁文件中的數據。這樣,當一個應用程序通過調用Vi r t u a l A l l o c函數,將物理存儲器提交給地址空間的一個區(qū)域時,地址空間實際上是從硬盤上的一個文件中進行分配的。系統(tǒng)的頁文件的大小是確定有多少物理存儲器可供應用程序使用時應該考慮的最重要的因素, R A M的容量則影響非常小。
第一種情況中,線程試圖訪問的數據是在R A M中。在這種情況下, C P U將數據的虛擬內存地址映射到內存的物理地址中,然后執(zhí)行需要的訪問。線程試圖訪問的數據不在R A M中,而是存放在頁文件中的某個地方。這時,試圖訪問就稱為頁面失效, C P U將把試圖進行的訪問通知操作系統(tǒng)。這時操作系統(tǒng)就尋找R A M中的一個內存空頁。如果找不到空頁,系統(tǒng)必須釋放一個空頁。如果一個頁面尚未被修改,系統(tǒng)就可以釋放該頁面。但是,如果系統(tǒng)需要釋放一個已經修改的頁面,那么它必須首先將該頁面從R A M拷貝到頁交換文件中,然后系統(tǒng)進入該頁文件,找出需要訪問的數據塊,并將數據加載到空閑的內存頁面。然后,操作系統(tǒng)更新它的用于指明數據的虛擬內存地址現在已經映射到R A M中的相應的物理存儲器地址中的表。這時C P U重新運行生成初始頁面失效的指令,但是這次C P U能夠將虛擬內存地址映射到一個物理R A M地址,并訪問該數據塊。
當閱讀了上一節(jié)后,你必定會認為,如果同時運行許多文件的話,頁文件就可能變得非常大,而且你會認為,每當你運行一個程序時,系統(tǒng)必須為進程的代碼和數據保留地址空間的一些區(qū)域,將物理存儲器提交給這些區(qū)域,然后將代碼和數據從硬盤上的程序文件拷貝到頁文件中已提交的物理存儲器中。
實際上系統(tǒng)并不進行上面所說的這些操作。如果它進行這些操作的話,就要花費很長的時間來加載程序并啟動它運行。相反,當啟動一個應用程序的時候,系統(tǒng)將打開該應用程序的. e x e文件,確定該應用程序的代碼和數據的大小。然后系統(tǒng)要保留一個地址空間的區(qū)域,并指明與該區(qū)域相關聯(lián)的物理存儲器是在. e x e文件本身中。即系統(tǒng)并不是從頁文件中分配地址空間,而是將. e x e文件的實際內容即映像用作程序的保留地址空間區(qū)域。當然,這使應用程序的加載非常迅速,并使頁文件能夠保持得非常小
一、開始之前,讓我們來了解一下Windows中內存管理的一些知識:
1. 機器的物理內存由兩部分組成。一部分為機器的主存RAM,也就是我們內存條的大小;另一部分為虛擬內存,它就在機器的硬盤上,以頁文件的形式存在。
2. 每個進程都有自己的虛擬地址空間,對于具有32位尋址能力的機器來說,這個虛擬空間的大小為4GB?,F在我們使用的機器就是4GB。
3. 進程的4GB虛擬地址空間又可以分成幾個部分,其中進程真正私有的空間少于2GB(這段地址空間被稱作“用戶方式分區(qū)”),其余的2GB多空間都是給操作系統(tǒng)的,且這部分空間被所有的進程共享。(參考Windows核心編程Chapter 13)
4. 為進程“分配內存”,這個概念可以細化:“保留一段地址空間”,“提交一段內存空間”,“將內存空間映射到主存”。在程序中我們通常所訪問的地址都必須是進程地址空間中被保留和提交的那段地址空間。
4.1 “保留一段地址空間”:即從進程的4GB地址空間中保留一段地址空間,這個過程通過VirtualAlloc函數完成,并把分配類型參數設置為MEM_RESERVE。這段空間的起始地址必須是系統(tǒng)分配粒度的整數倍,大小必須是系統(tǒng)頁面大小的整數倍。
4.2 “提交一段內存空間”:即為進程已保留的地址空間映射機器的物理內存,這里要特別注意,所謂物理內存一般并不是機器的主存,而只是機器的虛擬內存。這個過程同樣又VirtualAlloc完成,只是把分配類型參數設置為MEM_COMMIT。這段空間的起始地址和大小都必須是頁面大小的整數倍。這樣進程的對應被提交的區(qū)域就被映射到機器的虛擬內存上。
4.3 “將內存空間映射到主存”:這點很重要,操作系統(tǒng)總是只有在進程提交的頁面被訪問時才將相應的頁面加載到主存中,同時修改進程對應頁面的地址空間映射。這時,進程的地址空間中的對應區(qū)域才和機器上的主存對應起來。
Virtual Size:
該指標記錄了當前進程申請成功的其虛擬地址空間的總的空間大小,包括DLL/EXE占用的地址和通過VirtualAlloc API Reserve的Memory Space數量。請注意,該指標包括保留的地址空間。
Private Bytes:
該指標記錄了進程用戶方式分區(qū)地址空間中已提交的總的空間大小。無論是直接調用API申請的內存,被Heap Manager申請的內存,或者是CLR 的managed heap,都算在里面。
Working Set:
該指標記錄了所有映射到進程虛擬地址空間的機器主存的大小,它不僅僅是用戶方式分區(qū)部分的映射,而是整個進程地址空間的映射。即它同時包括內核方式分區(qū)中映射到機器主存的部分。由4.3可知,在用戶方式分區(qū)部分只有在進程提交的頁面被訪問時才將相應的頁面加載到主存中。而對于該部分的大小總是系統(tǒng)頁面大小的整數倍。
這里有一個問題,隨著進程的不斷運行,進程被訪問的頁面將可能不斷增加,這是否意味著“Working Set”的大小會不斷的累加呢?顯然不是。在程序運行過程中影響“Working Set”的因素包括:(1) 機器可用主存的大小 (2) 進程本身“Working Set”的大小范圍。當機器的可用主存小于一定值時,系統(tǒng)會釋放一些老的最近沒有被訪問的頁面,把這些頁面通過交換文件交換到機器的虛擬內存中;當Working Set的大小大于該進程所設置的最大值時,同樣會把一些老的頁面交換到機器的虛擬內存中。當這些頁面下次再被訪問時,它們才加載到主存。
由上可知,”Working Set“一定比”Private Bytes“小,因為它只是”Private Bytes“對應的地址空間中被加載到主存的那部分。
“Page Faults”
該指標和”Working Set“密切相關,當進程訪問某個頁面,而這個頁面卻不在主存中時,就要發(fā)生一次“Page Fault“,即進程訪問非”Working Set“中的頁面時,發(fā)生一次”Page Fault“,同時系統(tǒng)將對應頁面加載到主存中。
接下來的三個指標是對”Working Set“的細化:
”WS Private“
該指標記錄了進程”Working Set“中被該進程所獨享的空間大小。
"WS Shareable"
該指標記錄了進程”Working Set“中能與別的進程共享的空間大小
”WS Shared“
該指標記錄了進程”Working Set“中已經與別的進程共享的空間大小
”WS Shareable“和”WS Shared“兩個指標乍一看令人感到疑惑,因為既然”Working Set“屬于”Private Bytes“中的一部分,而”Private Bytes“是進程私有的,為什么會有”WS Shareable“和”WS Shared“這兩項呢?
認真一想,其實很容易理解,比如兩個進程都需要同一個DLL的支持,所以在進程運行過程中,這個DLL被映射到了兩個進程的地址空間中,如果這個DLL的大小為4K,在兩個進程中都要提交4K的虛擬地址空間來映射這個DLL。當第一個進程訪問了這個DLL時,這個DLL被加載到機器主存中,這時,第二個進程也要訪問該DLL,這時,系統(tǒng)就不會再加載一遍該DLL了,因為這個DLL已經在主存中了。當然上面所說的訪問僅僅是讀取的操作,如果這時候某個進程要修改DLL對應這段地址中的某個單元時,這時,系統(tǒng)必須為第二個進程分配另外的新頁面,并把要修改位置對應的頁面拷貝的這個新頁面,同時,第二個進程中的這個DLL被映射到這個新頁面上。
上面的分析中,DLL對應的4K的內存在第一個進程中便是”WS Shareable“。另外,內核方式分區(qū)中的所有代碼都是被所有進程共享的,只要一個進程訪問了這些頁面,則在所有的進程的”Working Set“中都能體現。
三、下面我們來討論一下這些內存指標與進程內存消耗之間的關系
在計算機更新?lián)Q代不斷加速的今天,我們往往很少關注程序對內存的消耗,除非程序的內存消耗超出了我們的忍受范圍——大量的泄漏、運行速度下降等。
那么,當我們在測進程的內存使用量時,到底應該使用哪個指標能更好的反應程序的內存消耗呢?由于Windows自帶的Task Manager中的”Memory Usage“所對應的指標就是”Working Set“,所以大部分人認為該指標能夠很好的反應進程的內存使用量。
在得出結論之前,讓我們來分析一下以上的這些指標:
就從”Working Set“開始吧。
”Working Set“:
進程中被加載到機器主存的所有頁面大小的和。它可細分為”WS Shareable“和”WS Shared“。進程訪問頁面不再”Working Set“中時,會發(fā)生一次”Page Fault“且同時發(fā)生一次主存與虛擬內存之間的數據交換。綜上所述,我們可以得出結論:
(a)”Working Set“不是進程內存消耗的全部;
(b)所有進程”Working Set“的和也不等有機器主存總的消耗量,因為存在”Working Shareable“與別的進程共享;
(c)”Working Set“太大會影響機器的運行速度,因為”Working Set“太大會導致機器的可用主存太少,從而導致將進程的老頁面釋放到虛擬內存,同時,進程”Working Set“中的頁面減少后,使進程發(fā)生”Page Fault“的頻率更高。因為在主存與虛擬內存之間交換數據需要時間,所以機器的運行速度要減慢。
(d)”Working Set“由于數據交換的存在,該指標是動態(tài)的,在測量的過程中會不斷變化。(變化的最小單位為4K)
所以”Working Set“指標強調的是進程對機器主存的消耗,不是進程內存的全部信息。
"Private Bytes"
該指標包含所有為進程提交的內存,包括機器主存和虛擬內存,可以認為它是進程對物理內存消耗,且該指標相對來說更加穩(wěn)定。在程序產生內存泄漏時,該值一定是不斷上漲的。
綜上所述,個人更傾向于使用”Private Bytes“來定量進程的內存消耗和分析進程的內存泄漏。
本站僅提供存儲服務,所有內容均由用戶發(fā)布,如發(fā)現有害或侵權內容,請點擊舉報。
打開APP,閱讀全文并永久保存 查看更多類似文章
猜你喜歡
類似文章
《Windows核心編程系列》十二談談Windows內存體系結構
系統(tǒng)設計硬核知識(3)——操作系統(tǒng)的存儲管理
虛擬內存,內存頁面錯誤與頁面錯誤增量如何處理。(整理)
全面介紹Windows內存管理機制及C++系列 之 內存分配實例
存儲管理
操作系統(tǒng)第4章
更多類似文章 >>
生活服務
分享 收藏 導長圖 關注 下載文章
綁定賬號成功
后續(xù)可登錄賬號暢享VIP特權!
如果VIP功能使用有故障,
可點擊這里聯(lián)系客服!

聯(lián)系客服