內存是什么?
內存對于用戶來說就是一個字節數組,我們可以根據地址來訪問到某個字節或者某些字節:

很久之前的內存
很久很久之前,一臺機器上只放置一個程序,操作系統僅僅作為一個函數庫存在。對于內存來說,除去操作系統的代碼和數據占用的一些空間外,其余空間全部分配給正在運行的那個程序,畫個圖就是這樣:

我們把運行著的程序稱之為進程。
同時運行多個程序的內存
后來人們覺得同時在一臺計算機上只運行一個程序太虧了,就設計了一個可以同時運行多個程序的機制。不過內存條只有一個,所以這些用戶程序只能共享同一個內存條,只能把內存的不同部分劃分給不同的用戶程序,畫個圖就像是這樣:

這樣子的話也有一些問題:
- 不同用戶程序只能使用給他們規定好的那部分內存,也就是程序員在敲代碼的時候就應該小心翼翼的計算自己使用的內存有沒有占到別人家的地兒,這樣對碼農很不友好有木有。
- 如果哪個心眼兒壞的家伙故意去讀取別人家的程序使用的內存,這不就暴露了么,更嚴重的,這個壞家伙直接把別人家程序正在使用的內存的某些字節給更新掉,這就是天坑了~
- 能使用的內存空間都給規定好了,太少了有木有~
虛擬內存
操作系統是個老好人,使命就是解決所有用戶感到麻煩的事情。設計操作系統的大叔覺得,如果讓用戶直接去操作內存的話,用戶也不知道其他人到底使用了內存的哪些字節,自己可以使用哪些字節,如果要做到這一點的話人們在開發程序的時候還要聚到一起開個會,把每個人該用哪塊內存都規定好,即使規定好了用戶還得小心翼翼的避免使用了不屬于自己的那部分內存。干脆,干脆就不讓用戶直接操作內存了,讓用戶在編程序的時候直接把內存想象成一個非常非常大的字節數組就好了,,自己在這個字節數組上可以隨便折騰,他們把這個非常大的字節數組稱之為虛擬內存,由操作系統完成從虛擬內存的虛擬地址到真實內存的真實地址之間的映射工作。畫個圖就像這樣:

這樣還有問題,用戶越來越多,即使每個用戶都使用非常少的內存空間,那加起來占用的內存空間都可能超過了真實內存的大小,更何況某些喪心病狂的程序員寫的程序里本身就使用了超過真正內存大小的空間,這可怎么辦。這難不倒設計操作系統的大叔們,他們機智的把硬盤也拉了進來。

操作系統完成由虛擬內存地址到真實內存地址或者磁盤地址之間的映射工作,這樣子給用戶提供的虛擬內存的地址空間就可以非常非常大,用戶程序中那些很久都用不到的內存空間可以被操作系統給搞到磁盤上邊存儲,什么時候需要用了,又從磁盤中加載到真實內存中,重要的是這個過程全部是操作系統自動完成的,對于我們這些碼農來說,編寫程序就像是在一個炒雞大的字節數組上將某個地方的數據搬到另一個地方,或者將某個地方的數據經過CPU的某種加工之后再放到某個字節數組的某個地方,整個過程so easy,完全不用擔心有沒有讀取/覆蓋掉別人正在使用的內存空間。
對于windows操作系統來說,虛擬內存所需的磁盤空間從系統盤,一般是C盤分配,當你的C盤剩余空間很少時就會導致很卡~
虛擬內存結構
我們說對于敲代碼的碼農來說,虛擬內存就是一個超級大的字節數組,不過這個字節數組也是被劃分為多個部分的,并不是每個地方用戶都可以訪問,一般的虛擬內存結構長這樣:

用戶需要調用函數的時候,一般都會生成一個稱之為棧幀的結構,從而導致虛擬內存中的棧空間增長。
用戶需要單獨申請內存空間時,就會從堆空間分配,從而導致虛擬內存中的堆空間增長。
這里需要強調的一點是,我們平時所說的操作系統內核不是一個進程,而是指操作系統的代碼。