Memory Management (II) - Test Result
摘要提示
- 測試動機: 以自寫 C 程式在 Windows 上重現並驗證記憶體碎片化與可定址空間對配置失敗的影響。
- 測試環境: 比較 x86、x86 + LargeAddressAware(於 x64 上執行)、純 x64 三種情境。
- 可定址空間: x86 預設應用程式可用 2GB;加上 LargeAddressAware 於 x64 可用近 4GB;x64 有高達 8TB 虛擬空間。
- 實際可用: 在測試機(2GB RAM + 4GB swap)下,實際可配置約 1.9GB、3.9GB、4.0GB(受總可用實體+分頁限制)。
- 碎片化影響: 當虛擬位址空間不足時,大區塊配置容易因碎片化失敗;位址空間極大時碎片化影響顯著降低。
- 測試結果: 72MB 大塊配置在 x86 與 x86+LAA 失敗(各僅 2 塊),在 x64 成功(達 27 塊)。
- 關鍵結論: 成敗不取決於 32/64 位或 OS 種類,而是虛擬位址空間是否「用完/被分散」。
- /3GB 與 LAA: /3GB 可將 x86 應用位址空間擴至 3GB;需搭配 /LARGEADDRESSAWARE 編譯參數。
- OS 無法幫你重整: 允許指標語言(C/C++)下,OS 不能搬動區塊做 defrag,否則指標失效。
- 對服務程式啟示: 長時間運行的 32 位元服務更易踩雷;GC 語言的處理留待下篇。
全文重點
作者為驗證記憶體管理與碎片化在現代作業系統上的實際影響,撰寫 C 測試程式於 Windows 平台進行三種情境的對照實驗:x86(WOW64)、x86 + /LARGEADDRESSAWARE(WOW64)與原生 x64。測試機器為 Windows Server 2003 x64,2GB RAM 搭配 4GB 分頁檔。測試程序先大量配置 64MB 區塊,再釋放其中一半,最後嘗試配置多個 72MB 區塊,用以觀察在同總量下,因為虛擬位址空間碎片化導致的大塊配置失敗現象。
量測結果顯示:在 x86 預設下,應用程式可定址空間為 2GB,實際約能用到 1.9GB;於 x64 上執行的 x86 應用若未加 LAA 仍僅能用 2GB,可加入 LAA 後在 x64 上可用到近 4GB;原生 x64 則擁有高達 8TB 的虛擬位址空間。然而實際可配置量仍受限於實體記憶體加分頁檔(本例合計約 6GB,扣掉系統與其他程式後,測到約 4GB 左右)。在相同配置/釋放步驟下,x86 與 x86+LAA 皆無法成功連續配置 72MB 的大塊(各只成功 2 塊),而 x64 則可配置到 27 塊。差異的核心不是作業系統與 CPU 位數本身,而是虛擬位址空間是否已經被各種配置分散佔滿,導致無法找出足夠大的連續洞給大區塊。
理論上,分頁機制無法解決虛擬位址空間碎片化;若 OS 或程式未做適當 defrag,當要求較大連續區塊時必然容易失敗。x64 的成功關鍵,在於其虛擬位址空間巨大(8TB),遠大於實際可用記憶體總量,導致「實際可用量先用盡」而非「位址空間先碎裂用完」,因此幾乎不太受碎片化影響。作者強調,在實務上,當應用可定址空間接近實際可用量時,碎片化問題更易浮現;反之,若位址空間極大,通常只需關注總配置量是否超過可用記憶體即可。
此外,Windows 的 /3GB 與 /LARGEADDRESSAWARE 機制提供 x86 應用放寬位址空間的方式,但仍遠不及 x64 的虛擬空間優勢。OS 無法替 C/C++ 類語言進行記憶體 defrag,因為會破壞指標的有效性與語意,故此類問題需由應用自行管理。作者最後點出:這不是典型記憶體洩漏(已釋放),而是配置策略與位址空間限制交互造成的結果;至於 .NET/Java 的 GC 是否面臨同樣困境,將在下一篇討論。
段落重點
前言與動機
作者自嘲在冷門主題上做測試,但為了把過去心得在主流 OS 與平台上重新驗證,重新動手以 C 撰寫測試程式,並順道研究 x64 編譯與相關設定。此次只測 Windows,原因是精力有限;歡迎他人協助測其他 OS 並共享結果。核心問題並非單純的 Windows/Linux 或 32/64 位之別,而是「虛擬位址空間是否被用完或被碎片化」。因此作者設計一個能製造大區塊配置壓力的程式,用以觀察碎片化對大配置的影響。
測試設定與環境
測試環境統一為 Windows Server 2003 x64,實體記憶體 2GB、分頁檔 4GB。比較三個情境:1) x86(於 x64 的 WOW64 上跑)、2) x86 + /LARGEADDRESSAWARE(同樣於 x64 上跑)、3) 原生 x64。原計畫加入 /3GB,但與情境 2 趨同(只是 3GB vs 4GB 可用空間),遂省略。測試程式流程為:大量配置 64MB 區塊、釋放其中一半、再嘗試配置 72MB 區塊,並印出配置數與地址範圍,以觀察是否能取得連續的大塊空間。
測試結果與表格整理
摘要結果:x86 可定址空間 2GB,實際約 1.92GB;x86+LAA 於 x64 上可定址 4GB,實際約 3.9GB;x64 可定址高達 8TB,但受實體+分頁上限實際僅約 4.0GB。本次關鍵測試「能否再配置 72MB 大塊」:x86 與 x86+LAA 皆失敗(各僅 2 塊),x64 成功(27 塊)。這顛覆許多人直覺,顯示問題不在 OS 或 CPU 位數本身,而是位址空間是否足夠大到不致先被碎片化壓垮。當虛擬空間遠大於實際可用記憶體時,碎片化不易成為瓶頸。
原因解析:位址空間、實體/虛擬記憶體與碎片化
分頁管理無法自動修補虛擬位址空間的碎片化;若應用不做整理,大塊配置就可能卡在找不到足夠大的連續洞。x64 通關的原因並非更聰明,而是其 8TB 虛擬空間相對於實際 6GB(2GB RAM + 4GB 分頁)大到離譜,導致實際可用量會先用盡,位址空間幾乎不可能「用滿」到需要為大塊騰出連續洞。反之,x86 的 2GB 或 3–4GB 空間很容易被各種分配打散,當你嘗試配置 72MB 這類大區塊時,就算總量足夠,也會因破碎而失敗,形成「Out Of Memory」的誤解。
對 32/64 位元與長時間服務程式的啟示
現代 PC 的可用記憶體(含分頁)動輒超過數 GB,寫得不好的記憶體使用模式,確實會在 32 位元長時間運行的服務或伺服器應用中引爆:明明總量未滿卻配置失敗。這不是記憶體洩漏(釋放有做),而是碎片化與位址空間上限共同造成。當可定址空間逼近實際可用量時,碎片化風險大增;若切換到 x64,通常只需關注總配置量是否超過可用記憶體。至於 GC 語言(Java/.NET)是否能逃過此劫,作者賣關子,留待下篇深入。
註解重點:/LARGEADDRESSAWARE、OS 為何不做記憶體重整、測試程式
註 1 說明:x86 理論 4GB 中,Windows 將 2GB 給核心、2GB 給應用;/3GB 可調為 1GB:3GB,但應用需以 /LARGEADDRESSAWARE 編譯。在 x64 上,未加 LAA 的 x86 應用仍只有 2GB;加 LAA 可用近 4GB。註 2 指出 OS 不能替 C/C++ 做記憶體 defrag,否則指標全部失效,語義破壞,因此這類語言必須自行設計配置策略(如池化、避免大塊反覆配置等)。註 3 提供調整後的測試程式碼(64MB/72MB 版本)供跨平台實驗。
測試程式概要
程式以三階段執行:1) 連續配置多個 64MB 區塊(buffer1 與 buffer2 交錯配置)以填滿虛擬位址空間;2) 釋放其中一半(buffer2),製造「總量足夠但連續空間不足」的碎片化情境;3) 嘗試配置多個 72MB 區塊(buffer3),驗證在不同可定址空間下,大區塊是否能找到連續洞。最後印出各塊地址範圍,協助觀察分佈與碎片化實況。此流程逼真反映長期運行應用的典型模式:反覆配置/釋放大小不一的塊,逐步累積碎片,最終在大塊需求時失敗。
資訊整理
知識架構圖
- 前置知識:
- 虛擬記憶體與實體記憶體的差異與關係(Physical RAM、Swap/Pagefile)
- 位址空間與可定址上限(32 位元 vs 64 位元)
- 記憶體配置與釋放的基本概念(malloc/free、碎片化)
- Windows 特有參數與編譯選項(/3GB、/LARGEADDRESSAWARE、WOW64)
- 核心概念:
- 虛擬位址空間碎片化:大量小/中區塊配置釋放造成的大區塊無法配置
- 位址空間上限與實際可用量:可定址上限不等於可配置成功量,還受碎片與總可用記憶體制約
- 平台/建置差異:x86、WOW64 + LargeAddressAware、x64 對位址空間與可配置塊大小的影響
- OS 無法替可被指標直接操作的記憶體做 Defrag:指標語言的限制
- 測試設計與觀察:以 64MB 塊製造碎片,再嘗試配置 72MB 大塊驗證是否失敗
- 技術依賴:
- 作業系統位址空間規劃(32 位元預設 2GB user/2GB kernel,/3GB 可調 3GB/1GB;在 x64 上 WOW64 可到 4GB 需 LargeAddressAware)
- 編譯器與連結器選項(x86 vs x64、/LARGEADDRESSAWARE)
- 記憶體管理機制(paging 不能解決虛擬位址碎片)
- 語言層能力(可直接操作指標的 C/C++ 無法由 OS 搬動已配置區塊)
- 應用場景:
- 長時間運作的 32 位元服務/伺服器程式,易遭遇位址空間碎片導致的大區塊配置失敗
- 需配置連續大區塊的應用(影像/影片處理、快取池、大資料 buffer)
- 從 x86 遷移到 x64 以緩解碎片問題與擴展位址空間
- 在 x64 OS 上運行舊 x86 應用,藉由 LargeAddressAware 提升可用位址空間
學習路徑建議
- 入門者路徑:
- 了解虛擬記憶體 vs 實體記憶體、位址空間上限(32/64 位)
- 讀懂 Windows 的 2G/2G 與 /3GB 規則、WOW64 概念
- 練習用簡單 C 程式配置/釋放記憶體,觀察碎片化現象
- 進階者路徑:
- 掌握 /LARGEADDRESSAWARE 的作用與在 x64 OS 下的差異
- 設計測試(如文中 64MB/72MB 模式)量測大區塊配置成功率
- 分析程序虛擬位址空間分布(如使用 VMMap、Process Explorer)
- 實戰路徑:
- 對需要大區塊的模組進行池化/分段/對齊策略,降低碎片
- 對 x86 應用啟用 LargeAddressAware,並於 x64 OS 部署
- 規畫遷移到原生 x64(若可行),從源頭減少位址空間不足
- 建立監控:追蹤虛擬位址空間耗用、配置失敗點與堆區碎片情況
關鍵要點清單
- 虛擬位址空間碎片化:連續大區塊配置失敗的主因常是碎片,而非總記憶體不足(優先級: 高)
- 可定址上限與可用量:位址上限(如 2GB/3GB/4GB/8TB)不等於實際可配置,仍受碎片與總可用記憶體限制(優先級: 高)
- 測試結論(x86 vs WOW64 vs x64):x86 與 WOW64 即便可用 4GB,仍可能因碎片配置失敗;x64 因位址空間極大,72MB 連續塊顯著較易成功(優先級: 高)
- /LARGEADDRESSAWARE:讓 x86 應用在 x64 OS 上突破 2GB 到約 4GB 位址空間的關鍵編譯選項(優先級: 高)
- /3GB 參數:在 32 位元 OS 上將 user/kernel 調為 3GB/1GB,需配合 LargeAddressAware(優先級: 中)
- WOW64 行為:x64 OS 上執行 x86 應用,預設 2GB;LargeAddressAware 可至約 4GB,但仍可能受碎片影響(優先級: 高)
- OS 不能幫你 Defrag 記憶體:C/C++ 指標語言下,OS 不能搬動已配置區塊,否則指標失效(優先級: 高)
- Paging 不是碎片解藥:分頁機制無法解決虛擬位址空間的連續區塊需求(優先級: 中)
- 實際可用記憶體上限:實體 RAM + 分頁檔形成上限,位址空間再大也不能超過總可用記憶體(優先級: 中)
- 長時服務風險:長時間運行的 32 位元服務更易累積碎片,最後在大塊配置時失敗(優先級: 高)
- 大盒子效應:x64 巨大位址空間可緩解碎片問題,除非接近總可用記憶體才會再現(優先級: 中)
- 配置策略:盡量預留/對齊/池化大塊,避免交錯大小配置造成碎片(優先級: 中)
- 監控與診斷:使用工具觀察 VA 佈局與堆碎片,定位失敗點(優先級: 中)
- 移植決策:對需大區塊或大記憶體需求的應用,優先考慮原生 x64(優先級: 高)
- GC 語言提示:垃圾回收不等同於碎片免疫,仍需設計以避免大塊配置風險(優先級: 中)