本機內存簡介:操作系統,硬件限制及虛擬內存
本文介紹操作系統和底層硬件給本機內存帶來的限制。
硬件限制
本機進程遇到的許多限制都是由硬件造成的,而與操作系統沒有關系。每臺計算機都有一個處理器和一些隨機存取存儲器(RAM),后者也稱為物理內存。處理器將數據流解釋為要執行的指令,它擁有一個或多個處理單元,用于執行整數和浮點運算以及更高級的計算。處理器具有許多寄存器 —— 常快速的內存元素,用作被執行的計算的工作存儲,寄存器大小決定了一次計算可使用的***數值。
處理器通過內存總線連接到物理內存。物理地址(處理器用于索引物理 RAM 的地址)的大小限制了可以尋址的內存。例如,一個 16 位物理地址可以尋址 0x0000 到 0xFFFF 的內存地址,這個地址范圍包括 2^16 = 65536 個惟一的內存位置。如果每個地址引用一個存儲字節,那么一個 16 位物理地址將允許處理器尋址 64KB 內存。
處理器被描述為特定數量的數據位。這通常指的是寄存器大小,但是也存在例外,比如 32 位 390 指的是物理地址大小。對于桌面和服務器平臺,這個數字為 31、32 或 64;對于嵌入式設備和微處理器,這個數字可能小至 4。物理地址大小可以與寄存器帶寬一樣大,也可以比它大或小。如果在適當的操作系統上運行,大部分 64 位處理器可以運行 32 位程序。
表 1 列出了一些流行的 Linux 和 Windows 架構,以及它們的寄存器和物理地址大小:
表 1. 一些流行處理器架構的寄存器和物理地址大小
架構 | 寄存器帶寬(位) | 物理地址大小(位) |
---|---|---|
(現代)Intel® x86 | 32 | 32 36,具有物理地址擴展(Pentium Pro 和更高型號) |
x86 64 | 64 | 目前為 48 位(以后將會增大) |
PPC64 | 64 | 在 POWER 5 上為 50 位 |
390 31 位 | 32 | 31 |
390 64 位 | 64 | 64 |
操作系統和虛擬內存
如果您編寫無需操作系統,直接在處理器上運行的應用程序,您可以使用處理器可以尋址的所有內存(假設連接到了足夠的物理 RAM)。但是要使用多任務和硬件抽象等特性,幾乎所有人都會使用某種類型的操作系統來運行他們的程序。
在 Windows 和 Linux 等多任務操作系統中,有多個程序在使用系統資源。需要為每個程序分配物理內存區域來在其中運行。可以設計這樣一個操作系統:每個程序直接使用物理內存,并且可以可靠地僅使用分配給它的內存。一些嵌入式操作系統以這種方式工作,但是這在包含多個未經過集中測試的應用程序的環境中是不切實際的,因為任何程序都可能破壞其他程序或者操作系統本身的內存。
虛擬內存 允許多個進程共享物理內存,而且不會破壞彼此的數據。在具有虛擬內存的操作系統(比如 Windows、Linux 和許多其他操作系統)中,每個程序都擁有自己的虛擬地址空間 —— 一個邏輯地址區域,其大小由該系統上的地址大小規定(所以,桌面和服務器平臺的虛擬地址空間為 31、32 或 64 位)。進程的虛擬地址空間中的區域可被映射到物理內存、文件或任何其他可尋址存儲。當數據未使用時,操作系統可以在物理內存與一個交換區域(Windows 上的頁面文件 或者 Linux 上的交換分區)之間移動它,以實現對物理內存的***利用率。當一個程序嘗試使用虛擬地址訪問內存時,操作系統連同片上硬件會將該虛擬地址映射到物理位置,這個位置可以是物理 RAM、一個文件或頁面文件/交換分區。如果一個內存區域被移動到交換空間,那么它將在被使用之前加載回物理內存中。圖 1 展示了虛擬內存如何將進程地址空間區域映射到共享資源:
圖 1. 虛擬內存將進程地址空間映射到物理資源

程序的每個實例以進程 的形式運行。在 Linux 和 Windows 上,進程是一個由受操作系統控制的資源(比如文件和套接字信息)、一個典型的虛擬地址空間(在某些架構上不止一個)和至少一個執行線程構成的集合。
虛擬地址空間大小可能比處理器的物理地址大小更小。32 位 Intel x86 最初擁有的 32 位物理地址僅允許處理器尋址 4GB 存儲空間。后來,添加了一種稱為物理地址擴展(Physical Address Extension,PAE)的特性,將物理地址大小擴大到了 36 位,允許安裝或尋址至多 64GB RAM。PAE 允許操作系統將 32 位的 4GB 虛擬地址空間映射到一個較大的物理地址范圍,但是它不允許每個進程擁有 64GB 虛擬地址空間。這意味著如果您將大于 4GB 的內存放入 32 位 Intel 服務器中,您將無法將所有內存直接映射到一個單一進程中。
地址窗口擴展(Address Windowing Extension)特性允許 Windows 進程將其 32 位地址空間的一部分作為滑動窗口映射到較大的內存區域中。Linux 使用類似的技術將內存區域映射到虛擬地址空間中。這意味著盡管您無法直接引用大于 4GB 的內存,但您仍然可以使用較大的內存區域。
內核空間和用戶空間
盡管每個進程都有其自己的地址空間,但程序通常無法使用所有這些空間。地址空間被劃分為用戶空間 和內核空間。內核是主要的操作系統程序,包含用于連接計算機硬件、調度程序以及提供聯網和虛擬內存等服務的邏輯。
作為計算機啟動序列的一部分,操作系統內核運行并初始化硬件。一旦內核配置了硬件及其自己的內部狀態,***個用戶空間進程就會啟動。如果用戶程序需要來自操作系統的服務,它可以執行一種稱為系統調用 的操作與內核程序交互,內核程序然后執行該請求。系統調用通常是讀取和寫入文件、聯網和啟動新進程等操作所必需的。
當執行系統調用時,內核需要訪問其自己的內存和調用進程的內存。因為正在執行當前線程的處理器被配置為使用地址空間映射來為當前進程映射虛擬地址,所以大部分操作系統將每個進程地址空間的一部分映射到一個通用的內核內存區域。被映射來供內核使用的地址空間部分稱為內核空間,其余部分稱為用戶空間,可供用戶應用程序使用。
內核空間和用戶空間之間的平衡關系因操作系統的不同而不同,甚至在運行于不同硬件架構之上的同一操作系統的各個實例間也有所不同。這種平衡通常是可配置的,可進行調整來為用戶應用程序或內核提供更多空間。縮減內核區域可能導致一些問題,比如能夠同時登錄的用戶數量限制或能夠運行的進程數量限制。更小的用戶空間意味著應用程序編程人員只能使用更少的內存空間。
默認情況下,32 位 Windows 擁有 2GB 用戶空間和 2GB 內核空間。在一些 Windows 版本上,通過向啟動配置添加 /3GB
開關并使用 /LARGEADDRESSAWARE
開關重新鏈接應用程序,可以將這種平衡調整為 3GB 用戶空間和 1GB 內核空間。在 32 位 Linux 上,默認設置為 3GB 用戶空間和 1GB 內核空間。一些 Linux 分發版提供了一個 hugemem 內核,支持 4GB 用戶空間。為了實現這種配置,將進行系統調用時使用的地址空間分配給內核。通過這種方式增加用戶空間會減慢系統調用,因為每次進行系統調用時,操作系統必須在地址空間之間復制數據并重置進程地址-空間映射。圖 2 展示了 32 位 Windows 的地址-空間布局:
圖 2. 32 位 Windows 的地址-空間布局

圖 3 顯示了 32 位 Linux 的地址-空間配置:
圖 3. 32 位 Linux 的地址-空間布局

31 位 Linux 390 上還使用了一個獨立的內核地址空間,其中較小的 2GB 地址空間使對單個地址空間進行劃分不太合理,但是,390 架構可以同時使用多個地址空間,而且不會降低性能。
進程空間必須包含程序需要的所有內容,包括程序本身和它使用的共享庫(在 Windows 上為 DDL,在 Linux 上為 .so 文件)。共享庫不僅會占據空間,使程序無法在其中存儲數據,它們還會使地址空間碎片化,減少可作為連續內存塊分配的內存。這對于在擁有 3GB 用戶空間的 Windows x86 上運行的程序尤為明顯。DLL 在構建時設置了***的加載地址:當加載 DLL 時,它被映射到處于特定位置的地址空間,除非該位置已經被占用,在這種情況下,它會加載到別處。Windows NT 最初設計時設置了 2GB 可用用戶空間,這對于要構建來加載接近 2GB 區域的系統庫很有用 —— 使大部分用戶區域都可供應用程序自由使用。當用戶區域擴展到 3GB 時,系統共享庫仍然加載接近 2GB 數據(約為用戶空間的一半)。盡管總體用戶空間為 3GB,但是不可能分配 3GB 大的內存塊,因為共享庫無法加載這么大的內存。
在 Windows 中使用 /3GB
開關,可以將內核空間減少一半,也就是最初設計的大小。在一些情形下,可能耗盡 1GB 內核空間,使 I/O 變得緩慢,且無法正常創建新的用戶會話。盡管 /3GB
開關可能對一些應用程序非常有用,但任何使用它的環境在部署之前都應該進行徹底的負載測試。
本機內存泄漏或過度使用本機內存將導致不同的問題,具體取決于您是耗盡了地址空間還是用完了物理內存。耗盡地址空間通常只會發生在 32 位進程上,因為*** 4GB 的內存很容易分配完。64 位進程具有數百或數千 GB 的用戶空間,即使您特意消耗空間也很難耗盡這么大的空間。如果您確實耗盡了 Java 進程的地址空間,那么 Java 運行時可能會出現一些陌生現象,本文稍后將詳細討論。當在進程地址空間比物理內存大的系統上運行時,內存泄漏或過度使用本機內存會迫使操作系統交換后備存儲器來用作本機進程的虛擬地址空間。訪問經過交換的內存地址比讀取駐留(在物理內存中)的地址慢得多,因為操作系統必須從硬盤驅動器拉取數據。可能會分配大量內存來用完所有物理內存和所有交換內存(頁面空間),在 Linux 上,這將觸發內核內存不足(OOM)結束程序,強制結束最消耗內存的進程。在 Windows 上,與地址空間被占滿時一樣,內存分配將會失敗。
同時,如果嘗試使用比物理內存大的虛擬內存,顯然在進程由于消耗內存太大而被結束之前就會遇到問題。系統將變得異常緩慢,因為它會將大部分時間用于在內存與交換空間之間來回復制數據。當發生這種情況時,計算機和獨立應用程序的性能將變得非常糟糕,從而使用戶意識到出現了問題。當 JVM 的 Java 堆被交換出來時,垃圾收集器的性能會變得非常差,應用程序可能被掛起。如果一臺機器上同時使用了多個 Java 運行時,那么物理內存必須足夠分配給所有 Java 堆。
【編輯推薦】