Linux 記憶體管理:交換、OOM 殺手與 Cgroups

12 分鐘閱讀 - 2026年5月31日

hero section cover
目錄
  • Linux 記憶體管理解析:交換區、OOM 殺手與 cgroups
  • Linux 如何管理記憶體頁面
  • 設定交換空間
  • OOM 殺手
  • Cgroups 與記憶體限制
  • 依伺服器角色設定記憶體
分享

Linux swap、OOM 殺手和 cgroups 如何一起運作 - 包含資料庫、Web 伺服器和多租戶 VPS 主機的配置範例。

Linux 記憶體管理解析:交換區、OOM 殺手與 cgroups

Linux 處理記憶體的方式與多數作業系統不同。高記憶體使用率未必是問題——核心會主動利用閒置記憶體進行快取,以加速磁碟讀取。但當實際記憶體壓力累積時,有三種機制會介入運作:交換區、OOM 殺手與 cgroups。理解每種機制的運作方式及配置方法,將決定伺服器在負載下是能平穩降級,還是會毫無預警地當機。

Linux 如何管理記憶體頁面

每個程序都在其專屬的虛擬位址空間中運行,在 64 位元系統上最大可達 128 TB。核心透過頁表將這些虛擬位址映射至實體 RAM,並利用翻譯旁路快取 (TLB) 來快取最近的查詢。TLB 命中約需 1 奈秒;未命中則需耗時 20 至 100 奈秒,這在資料庫等記憶體密集型工作負載中會累積成顯著的延遲。

實體記憶體被劃分為 4 KB 的頁面,而核心將其分為兩類:

  • 檔案支援頁面 — 與磁碟上的檔案相關聯。核心可直接丟棄乾淨頁面,或將髒頁面寫入交換區,無需使用交換空間。
  • 匿名頁面 — 沒有對應檔案的堆疊與堆記憶體。這些頁面必須先寫入交換空間,核心才能釋放它們。

在記憶體需求高的伺服器上,若匿名頁面佔比過高,將導致換片作業過早介入。請留意 si (swap in) 和 so (swap out) 欄位 vmstat 1 — 若這些欄位持續顯示非零值,即是系統承受壓力的首要警訊。

監控時,請選用合適的工具:

工具最適用於關鍵指標
free -h快速全系統快照available 欄位
vmstat 1即時交換空間與 I/O 監控si, so
htop互動式按進程檢視記憶體條狀圖、程序清單
smem精確的每個進程使用情況USS(唯一集大小)
/proc/meminfo核心層級詳細資訊MemAvailable, Dirty, Slab

一個常見的錯誤:關注 free 欄位 free -h 欄位並驚慌失措。真正重要的是 available 欄位才是關鍵。它包含內核可根據需求從快取中回收的記憶體。若伺服器顯示僅剩 512 MB 可用記憶體,但「可用」記憶體達 5 GB,則無須擔憂。

當記憶體降至閾值以下時,核心的 kswapd 守護程序會開始在背景回收記憶體頁面。若仍不足,核心將進入直接回收模式,並阻塞程序直到頁面釋出為止。這正是延遲飆升的來源。請設定警示,當 MemAvailable 低於總記憶體的 10–15% 時設定警示,以便您有時間做出反應。


 

設定交換空間

交換空間(Swap)是一塊磁碟區域(可能是分割區或檔案),當記憶體(RAM)滿載時,核心會將閒置的匿名頁面移至此處。 兩者速度差距顯著:DDR4 記憶體的延遲約為 100 奈秒,而 NVMe SSD 約為 100,000 奈秒,SATA SSD 則接近 500,000 奈秒。交換空間是安全緩衝區,而非額外的記憶體。若伺服器持續依賴交換空間,則表示其存在記憶體問題,增加交換空間並無法解決此問題。

建議使用交換檔案而非交換分割區。前者調整大小較為簡便,且無需重新分割磁碟。

sudo fallocate -l 2G /swapfile
sudo chmod 600 /swapfile
sudo mkswap /swapfile
sudo swapon /swapfile

將檔案設定為 /etc/fstab 以確保資料在重新開機後仍能保留。 chmod 600 此步驟是必要的——任何從 RAM 換出(paged out)的資料皆可從交換區讀取,因此該檔案絕不能設為世界可讀。

建立交換區後,請調整 vm.swappiness。預設值 60 過於激進。對於大多數主機工作負載,您希望核心優先使用 RAM,並僅在萬不得已時才使用交換空間:

伺服器角色vm.swappinessvm.vfs_cache_pressure
一般網頁伺服器10–2050
資料庫 (MySQL/PostgreSQL)1–550
預設 (多數發行版)60100

關於交換空間的設定:對於處理偶發流量尖峰的 2 GB VPS,1–2 GB 已足夠。在具備 8 GB 或更多記憶體的系統上,通常設定 2–4 GB 的固定交換空間即可。其目的是為核心提供處理冷頁面的緩衝機制,而非擴展總可尋址記憶體。

在記憶體受限但 CPU 資源充足的伺服器上,zram 可在記憶體中建立壓縮的交換區,完全避免磁碟 I/O。若在多租戶 VPS 主機上,且 NVMe 儲存裝置由各租戶共用,值得考慮採用此方案。若交換區與資料庫檔案位於同一裝置上,請留意 I/O 爭用問題——頻繁的交換操作與高吞吐量的磁碟寫入難以共存。

OOM 殺手

當核心耗盡 RAM 和交換空間,且無法透過其他方式回收足夠記憶體時,OOM 殺手便會介入。它會使用 oom_badness() 函式對進程進行評分:

points = (rss_anon + rss_file + rss_shmem + swapents + pgtables_pages) + (oom_score_adj × totalpages / 1000)

得分最高的進程將被終止。該公式傾向於優先處理大型記憶體消耗者,且核心會檢查進程是否已在過去 5 秒內被終止,藉此避免在短時間內連續終止多個進程。

日誌中會出現兩類 OOM 事件:

  • 全局 OOM — 整個系統的 RAM 和交換空間已耗盡。日誌前綴為 Out of memory:
  • Cgroup OOM — 某個容器或服務觸及其 memory.max 限制。日誌前綴為 Memory cgroup out of memory:

檢視過去的 OOM 事件:

dmesg -T | grep -i "out of memory"
journalctl -k --grep="oom"

請留意 OOM 日誌中的 order 欄位。若數值大於 0,則表示記憶體碎片化而非完全耗盡 — 即使有可用記憶體,核心仍無法找到足夠的連續頁面。

您可以透過調整 /proc/<pid>/oom_score_adj。數值範圍為 -1000(永不終止)至 +1000(優先終止)。對於由 systemd 管理的服務,請在單元檔案中永久設定此值:

[Service]
OOMScoreAdjust=-1000

用於調整 OOM 行為的其他 sysctl 參數:

參數效果
vm.overcommit_memory0預設啟用超額分配模式
vm.overcommit_memory2嚴格模式;防止分配量超過 RAM × overcommit_ratio + swap
vm.panic_on_oom1重新啟動系統而非終止程序
vm.oom_kill_allocating_task1終止觸發 OOM 的程序,而非最大資源消耗者

若要進行主動監控,請查看 /proc/pressure/memory (壓力滯留資訊,自 4.20 版內核起提供)。觀察 some avg10 數值:低於 5% 表示系統健康,若持續高於 20% 則意味著 OOM 事件可能即將發生。若 allocstall 計數器 /proc/vmstat 中的計數器上升是另一項早期徵兆——它統計直接回收停滯的次數,這通常是 OOM 終止前的前兆。像 systemd-oomdearlyoom 等工具可在核心的 OOM 殺手觸發前,依據 PSI 閾值採取行動。

Cgroups 與記憶體限制

控制群組(cgroups)可讓您將程序組織成群組,並強制執行嚴格的資源限制。 此機制於 Linux 2.6.24 版本中引入,是 DockerPodmanKubernetesLXC 等容器執行環境的基礎。核心會追蹤每個 cgroup 的記憶體使用狀況,涵蓋匿名記憶體、檔案後援頁面及核心物件。若某個 cgroup 達到其限制,核心會回收該群組內的記憶體,或觸發 cgroup 範圍內的 OOM 終止程序。

Cgroup v1 與 v2 的主要差異在於其結構。v1 會將每個控制器(記憶體、CPU、I/O)分別掛載在 /sys/fs/cgroup/<controller>/下分別掛載,導致資源追蹤不一致。V2 則採用統一的層級結構,位於 /sys/fs/cgroup/。Kubernetes 在 1.25 版中預設切換至 v2,並於 1.31 版中停止支援 v1。

要檢查系統使用的版本:

stat -fc %T /sys/fs/cgroup/

cgroup2fs 表示為 v2; tmpfs 通常表示 v1。

功能Cgroup v1Cgroup v2
層級結構多個,依控制器而定單一、統一
硬性記憶體限制memory.limit_in_bytesmemory.max
軟性記憶體限制memory.soft_limit_in_bytesmemory.high (限流)
使用情況追蹤memory.usage_in_bytesmemory.current
壓力指標受限PSI 整合

cgroup v2 中的關鍵記憶體控制項:

參數類型說明
memory.max硬性限制超過此值將觸發 OOM 殺手
memory.high軟限制在達到硬性限制前,會限制分配並觸發資源回收
memory.low軟性保護低於此閾值的記憶體將最後被回收
memory.min硬性保護低於此級別的記憶體絕不會被回收
memory.swap.max交換空間限制設定為 0 以停用此 cgroup 的交換功能
memory.oom.group布林值若啟用,OOM 將一併終止 cgroup 中的所有程序

實用準則:設定 memory.high 約低於 memory.max ,讓核心在觸及硬性限制前有空間進行回收。在設定 memory.max時,請在應用程式的峰值使用量之上增加 20–30%,以涵蓋頁瀏覽緩存(此緩存會計入 cgroup 記憶體總量)。

透過 systemd 管理 cgroup,而非直接寫入 cgroup 檔案系統。使用如 MemoryMax=, MemoryHigh=,並使用 MemoryMin= 等 unit 檔案指令來設定持久性限制。若需快速測試:

systemd-run --scope -p MemoryMax=512M <command>

對於網頁伺服器工作池,設定 memory.oom.group=1 可確保當某個工作執行緒超過其限制時能乾淨地終止 — 不會留下任何孤兒程序。對於資料庫引擎, memory.min 可防止緩衝區池在系統整體資源壓力下被回收。

依伺服器角色設定記憶體

正確的記憶體設定取決於伺服器的運作用途。若將相同的設定套用至資料庫與 PHP 網頁伺服器,其中一方的運作將受到影響。

伺服器角色vm.swappinessOOM 策略Cgroup 政策
資料庫1–5保護 (OOMScoreAdjust=-900)使用 memory.min 來保護緩衝區池
Web/應用程式伺服器10–20預設透過 memory.max
背景工作執行程序60可終止 (OOMScoreAdjust=+200)透過以下方式進行流量控制 memory.high
多租戶 VPS60(搭配 zram)預設透過 memory.max

對於 MySQL 和 PostgreSQL,請分配 50–70% 的可用 RAM 給 innodb_buffer_pool_size,停用透明巨頁以減少延遲突增,並透過 OOMScoreAdjust=-900 在 systemd 單元檔案中保護該程序。

對於 PHP-FPM,請根據實際記憶體使用量設定工作執行緒池大小。每個工作執行緒通常佔用 30–100 MB。將分配的 RAM 除以平均工作執行緒大小,即可得出安全的 pm.max_children 值。使用 memory.max cgroups 中的來限制工作池大小。

對於寫入密集型工作負載,請將 vm.dirty_ratio 約 10%,並將 vm.dirty_background_ratio 設為 3%。此設定會更頻繁地沖洗髒頁,避免發生大規模的 I/O 停滯。

將參數儲存至 /etc/sysctl.d/90-memory.conf。執行期間套用的設定在重新開機後會遺失。

按角色分類的建議值摘要:

參數Web/應用程式伺服器資料庫伺服器
vm.swappiness10–201–5
vm.vfs_cache_pressure5050
vm.dirty_ratio15%10%
vm.min_free_kbytes6553665536
OOM 保護預設OOMScoreAdjust=-1000

如果您正在執行高密度工作負載,且需要具備足夠餘裕以正確執行這些政策的伺服器,FDC 的專用伺服器值得您考慮。

博客

本周特色

更多文章
Linux 記憶體管理:交換、OOM 殺手與 Cgroups

Linux 記憶體管理:交換、OOM 殺手與 Cgroups

Linux swap、OOM 殺手和 cgroups 如何一起運作 - 包含資料庫、Web 伺服器和多租戶 VPS 主機的配置範例。

12 分鐘閱讀 - 2026年5月31日

Prometheus 和 node_exporter 設定指南

15 分鐘閱讀 - 2026年5月29日

更多文章