WIndows爲每一個進程分配了4GB的虛擬地址空間,讓每一個進程都認爲本身擁有4GB的內存空間,4GB怎麼來的? 32位 CPU能夠取地址的空間爲2的32次方,就是4GB(正如16位CPU有20根尋址線全部擁有2的20次方的尋址空間同樣)數組
當咱們在Windows中雙擊一個應用程序圖標後,系統爲該應用程序建立一個進程,Windows使得每一個進程都擁有2GB的地址空間,這2GB地址空間用於程序存放代碼,數據,堆棧,自由存儲區(堆),另外2GB用於共享系統使用blog
前面的這些地址並非物理內存中的地址,而是該進程空間中的虛擬地址
虛擬空間只是Windows爲該進程分配的一個虛擬的地址空間,只有當其和物理內存相關聯後纔有意義
內存的分頁
每一個物理地址對應一個虛擬地址?1GB那頁表該有多長,因此將內存分頁管理,4K爲一頁,即4K就是一個最小單位。虛擬地址到物理地址的映射見圖,中間的那個就是頁表了。進程
如何映射?
進程被建立時會創建一個 虛擬內從到物理內存的映射表--------頁表,根據頁表能夠將虛擬內存和物理內存關聯起來ip
-----------------------------------------頁表如何工做,怎麼將虛擬地址關聯物理地址----------------------------------內存
虛擬內存是什麼?
就是把磁盤拿來當內存用,這是之前買電腦時的想法。
因此就一直都想不明白一個問題:要真是這樣,那內存分個什麼1GB,2GB,4GB,你們都買個1M的內存條,而後把本身磁盤拿來當內存用多好,比2GB,4GB不知道要大多少。
其實這個說法有一點擦邊球的味道,虛擬內存是一些系統頁文件,存放在磁盤上,每一個系統頁文件大小也爲4K,物理內存也被分頁,每一個頁大小也爲4K,這樣虛擬頁文件和物理內存頁就能夠對應,實際上虛擬內存就是用於物理內存的臨時存放的磁盤空間。
頁文件就是內存頁,物理內存中每頁叫物理頁,磁盤上的頁文件叫虛擬頁,物理頁+虛擬頁就是系統因此使用的頁文件的總和。還有映像頁文件和映射頁文件,映像頁文件就是拿程序自己當頁文件使用(而不是用系統的頁文件),映射頁文件就是使用磁盤上的文件(非系統頁文件)來當頁文件使用(這主要用於讀取文件)。get
虛擬地址頁的狀態:
空閒:該區域沒有被所使用,也沒有被預約,沒有和物理內存管理
私有:該區域雖然沒有被使用,可是已經被申請(預約了),別人沒法使用它。一樣也沒和物理內存關聯
提交:該區域已經和物理內存管理,可使用了it
虛擬內存和物理內存的管理(Windows內存管理的核心)
Windows是多任務的系統,在每一個進程建立時,系統爲每一個進程也建立了一個頁表,用於虛擬地址到物理地址的轉換。好比如今程序在執行進程A,用戶切換到了另一個進程B,則系統會將進程A在內存中的數據存放到頁文件中,並更新進程A的頁表(使虛擬地址和頁文件造成映射)。而後讀取進程B的頁表,根據頁表判斷進程B的數據是在內存中仍是在頁文件中(經過頁文件的類型來判斷),若是在內存中就直接讀取,若是在頁面文件中,就將頁面文件內容讀入物理內存,而後更新頁表(使虛擬地址和物理內存造成映射)。這樣一看,虛擬內存實際上就是冒牌的物理內存了吧。
程序的執行
一個PE文件有數據區,代碼區,堆棧區(由系統分配,用於管理局部變量),使用OD載入一個程序就能夠知道這些都是以二進制的形式保存在文件中。
程序剛運行的時候,系統不直接將整個程序載入到物理內存中,也不將其載入到頁文件中,而是以程序文件自己做爲頁文件造成映射(虛擬地址到頁文件的映射),創建頁表,而後隨着程序的執行經過頁表來將其虛擬地址轉換成物理地址(將頁文件讀入內存),而後在讀取內存中的指令或數據。當進程被切換時,將內存內容保存到頁文件,更新頁表,如此往復,實現多任務操做。內存管理
能夠知道,程序的代碼段,數據段,堆棧區(系統分配)這些虛擬地址區域已是映射狀態,即有相應的物理內存與之對應。系統爲每一個進程提供了2G的本身的虛擬地址空間,剩下的虛擬地址空間幹什麼用?
剩下的虛擬地址空間就是給程序運行時動態分配內存使用。C++中 new的功能就是動態分配地址空間:
申請內存的最小單位是區域,每一個區域爲CPU粒度大小,即64K,每次申請的內存都必須是64K的整數倍,C++ new功能申請一個區域,保留該區域,而後提交須要的頁,其餘的保留。變量
char *address=new char[1024]; //分配1K的內存
這條語句首先申請一個區域的地址空間,表示這個區域已經被預約了,這就是上述區域狀態中的私有狀態,雖然預約了,可是尚未和物理內存關聯起來,因此程序也沒法使用該內存,而後程序將這1K的內存提交,就是映射到了內存當中,區域的狀態就變成了映射狀態,這樣程序就可使用這1K的內存了,而剩下的頁仍然爲保留狀態。那當進程被切換時,這1K的進程存放在哪呢?程序自己的頁文件已經被 代碼,全局數據,堆棧這些所使用了,因此係統會爲自由存儲區分配的內存分配新的頁文件來作虛擬內存。cli
局部變量的定義是由系統分配的,它將局部變量分配到堆棧區,由於堆棧區已經映射了,因此不用在映射,故不用使用新的頁文件了。堆棧區的大小爲1M左右,若是分配的局部變量超過1M會產生堆棧溢出。
能夠看到,系統的單個頁文件大小爲4K,程序本身的虛擬空間地址從00010000到7FFEFFFF差很少是2G
動態分配一個500M的內存後,物理內存,頁文件,可用的虛擬地址空間都減小了500M
查詢內存狀態使用VirtualQuery(Address[n],&membaseinf,sizeof(MEMORY_BASIC_INFORMATION))
定義3個變量
char Stack[20*1024]; //存在堆棧中,堆棧在程序啓動時已經被映像到內存中了
char* Dynamic=new char[64*1024]; //動態分配一個70K的內存
char* Dynamic2=new char[1024]; //動態分配一個1K的內存
地址所在頁面基地址:查詢的地址所在的頁面的起始地址
頁面所在區域的基地址:頁面所在區域的起始地址
區域保護屬性:分配區域時要設置區域的讀寫屬性
從頁面基地址開始擁有相同屬性(空閒,保留,提交)的全部頁的字節數:能夠看到這些都是4096的整數倍,由於一個頁4096,該大小通常都和申請的內存空間大小至關,由於這些內存都被提交了。
申請一個內存空間的過程
首先申請一個虛擬地址空間區域,而後提交申請的內存空間大小的頁(將其和頁文件關聯)。其餘的地址空間保留。
第一條指令分配了一個字符數組的局部變量,該變量分配在堆棧中,由系統分配,因此其區域爲程序的靜態存儲區,即在程序啓動時候這個區域的全部虛擬地址就和程序文件自己映像了,因此局部變量的區域基地址都是同樣的,那爲何它的頁面文件類型是頁文件呢?不該該是exe映像麼?由於如今文件在內存中,全部是物理頁,就是頁文件。
第二條指令動態分配了一塊大小爲1K的內存區域,這塊內存分配在自由存儲區,它所在的區域是在堆中申請的一個區域,第三條指令在堆中分配了一個70K左右的內存,由於他們是在堆中分配的,因此這2個變量的區域基地址是不同的。
分配的區域有多大?
第三條指令分配了一個70K左右的內存,它會向系統申請多大的區域呢?由區域大小爲64K的整數倍知該區域至少爲128K,查詢這70K以後的虛擬地址的狀態
能夠看到該地址所在的區域和Dynamic是同樣的,它的基地址爲594000,在那70K以後,這以後的區域的狀態爲保留,說明系統保留了剩下的區域,這剩下的區域有966656,就是966K左右的大小,那整個區域的大小就是(0x14000)81920+966656。