Linux 記憶體管理:交換、OOM 殺手與 Cgroups
12 分鐘閱讀 - 2026年5月31日

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.swappiness | vm.vfs_cache_pressure |
|---|---|---|
| 一般網頁伺服器 | 10–20 | 50 |
| 資料庫 (MySQL/PostgreSQL) | 1–5 | 50 |
| 預設 (多數發行版) | 60 | 100 |
關於交換空間的設定:對於處理偶發流量尖峰的 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_memory | 0 | 預設啟用超額分配模式 |
vm.overcommit_memory | 2 | 嚴格模式;防止分配量超過 RAM × overcommit_ratio + swap |
vm.panic_on_oom | 1 | 重新啟動系統而非終止程序 |
vm.oom_kill_allocating_task | 1 | 終止觸發 OOM 的程序,而非最大資源消耗者 |
若要進行主動監控,請查看 /proc/pressure/memory (壓力滯留資訊,自 4.20 版內核起提供)。觀察 some avg10 數值:低於 5% 表示系統健康,若持續高於 20% 則意味著 OOM 事件可能即將發生。若 allocstall 計數器 /proc/vmstat 中的計數器上升是另一項早期徵兆——它統計直接回收停滯的次數,這通常是 OOM 終止前的前兆。像 systemd-oomd 或 earlyoom 等工具可在核心的 OOM 殺手觸發前,依據 PSI 閾值採取行動。
Cgroups 與記憶體限制
控制群組(cgroups)可讓您將程序組織成群組,並強制執行嚴格的資源限制。 此機制於 Linux 2.6.24 版本中引入,是 Docker、Podman、Kubernetes 及 LXC 等容器執行環境的基礎。核心會追蹤每個 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 v1 | Cgroup v2 |
|---|---|---|
| 層級結構 | 多個,依控制器而定 | 單一、統一 |
| 硬性記憶體限制 | memory.limit_in_bytes | memory.max |
| 軟性記憶體限制 | memory.soft_limit_in_bytes | memory.high (限流) |
| 使用情況追蹤 | memory.usage_in_bytes | memory.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.swappiness | OOM 策略 | Cgroup 政策 |
|---|---|---|---|
| 資料庫 | 1–5 | 保護 (OOMScoreAdjust=-900) | 使用 memory.min 來保護緩衝區池 |
| Web/應用程式伺服器 | 10–20 | 預設 | 透過 memory.max |
| 背景工作執行程序 | 60 | 可終止 (OOMScoreAdjust=+200) | 透過以下方式進行流量控制 memory.high |
| 多租戶 VPS | 60(搭配 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.swappiness | 10–20 | 1–5 |
vm.vfs_cache_pressure | 50 | 50 |
vm.dirty_ratio | 15% | 10% |
vm.min_free_kbytes | 65536 | 65536 |
| OOM 保護 | 預設 | OOMScoreAdjust=-1000 |
如果您正在執行高密度工作負載,且需要具備足夠餘裕以正確執行這些政策的伺服器,FDC 的專用伺服器值得您考慮。

Linux 記憶體管理:交換、OOM 殺手與 Cgroups
Linux swap、OOM 殺手和 cgroups 如何一起運作 - 包含資料庫、Web 伺服器和多租戶 VPS 主機的配置範例。
12 分鐘閱讀 - 2026年5月31日
Prometheus 和 node_exporter 設定指南
15 分鐘閱讀 - 2026年5月29日