福奇亞的記憶力重塑

大部分作業系統都採用記憶體宣告策略,確保 在任何時間點都能有效率地運用一系列執行程序 所有可用的實體記憶體作業系統具有固定數量 實體記憶體 (RAM) 以分配所有執行中的處理程序 這些都無法同時滿足所有需求

目前記憶體重回是最單純的一種形式,就是取代頁面, 並取代目前使用者活動的重要性 所以可能比較重要大多數作業系統都有一個免費網頁集區 以便快速完成連入記憶體配置 等待使用中的頁面釋出。

Fuchsia 採用類似的策略,讓系統盡可能 超過特定門檻的可用記憶體容量Fuchsia 使用 核心和使用者空間中的記憶體收回技術。這個 指南會說明這些記憶體回收技術的運作方式。以及 提供了一組工具,可用於分析及傾印記憶體用量 (請參閱 記憶體用量分析工具)。

清除分頁支援記憶體

使用者空間檔案系統 呼叫器 到檔案內分頁 例如磁碟等外部來源檔案系統會使用 VMO:頁面會由呼叫器服務填入頁面,在建立後 允許或拒絕要求

在 Fuchsia 中 blobfs 是 是不可變的檔案系統,此系統會透過呼叫器,代管所有執行檔, 讓系統視需求填入資料頁面系統處於記憶體壓力時,例如 可用的免費記憶體容量不足時,核心會剔除頁面 以取回記憶體。頁面存在於磁碟中 以便視需要再次擷取。

這個核心會追蹤所有透過分頁伺服器支援的記憶體。當可用記憶體不足時,應用程式會偵測 符合移除條件的對象。網頁有多個 LRU 追蹤 (最近最少 網頁佇列 (核心背景執行緒會定期輪替), 轉換為「age」網頁。其他背景執行緒會將最舊的網頁移除 排在記憶體壓力之下

隨著可用記憶體減少,核心會調整閒置和撤銷政策。 動畫越多越好 候選檔案。為了防止發生輾轉現象,MRU 中的網頁 (最近使用過) 網頁佇列一律不會撤銷。這個佇列的長度會因 系統的流失量

如果系統相對安靜,且記憶體用量穩定,核心 頁面讀取速度變慢,而且 MRU 佇列會累積更多頁面。另一方面 如果使用者在進行多項活動之間循環,請持續切換 那麼核心會嘗試更積極地跟上老舊的網頁。

使用者空間程序也可以使用 移除提示 來影響核心撤銷策略程序可以使用 DONT_NEED 能指出網頁已經不再使用,且適合 以及逐字生效他們也可以使用 ALWAYS_NEED 來表示網頁是重要網頁 不考慮移除,因此能避免擷取成本 下次再存取這些檔案時,即可讓這些執行個體繼續存取。

如要進一步瞭解移除提示,請參閱參考文件: zx_vmo_op_range敬上 和 zx_vmar_op_range

無重複頁面資料刪除

匿名 VMO (非分頁式) 中的頁面只會在 其二是權限,定義可執行的操作 例如讀取或寫入核心會使用單例模式零頁完成讀取作業。即使在 頁面會進行寫入,核心會嘗試刪除重複網頁, 因此只在 0 時傳回單例中的零,以便儲存 記憶體用量核心會定期以匿名的 VMO 掃描實體頁面, 有機會去除重複網頁

頁面表格宣告

如「位址空間」一文所述, VMAR 階層可協助核心追蹤虛擬與實體記憶體的對應情形。 首次存取虛擬位址時 系統會使用位址空間的 VMAR 樹狀結構來查詢基礎實體網頁。 虛擬與實體的對應關係接著儲存在硬體頁面表格 MMU 會使用這項資訊進行日後查詢 在記憶體壓力下 核心會收回硬體頁面資料表內一段時間未存取的記憶體。 如果需要這些對應 可以透過 VMAR 樹狀結構重新建構。

可捨棄的 VMO

使用者空間程序可能會產生 可捨棄的 VMO。 用戶端可以 鎖定與解鎖 不可卡片 視 VMO 是否使用而定。系統處於以下狀態時 記憶體壓力時,核心會找到已解鎖且釋放的可捨棄 VMO 具體做法是指示 Kubernetes 建立並維護 一或多個代表這些 Pod 的物件

程式碼範例 (模數錯誤處理):

// Create a discardable VMO.
zx_handle_t vmo;
uint64_t vmo_size = 5 * zx_system_get_page_size();
zx_vmo_create(vmo_size, ZX_VMO_DISCARDABLE, &vmo);

// Lock the VMO.
zx_vmo_lock_state_t lock_state = {};
zx_vmo_op_range(vmo, ZX_VMO_OP_LOCK, 0, vmo_size, &lock_state,
                sizeof(lock_state));

// Use the VMO as desired.
zx_vmo_read(vmo, buf, 0, sizeof(buf));

// Unlock the VMO. The kernel is free to discard it now.
zx_vmo_op_range(vmo, ZX_VMO_OP_UNLOCK, 0, vmo_size, nullptr, 0);

// Lock the VMO again before use.
zx_vmo_op_range(vmo, ZX_VMO_OP_LOCK, 0, vmo_size, &lock_state,
                sizeof(lock_state));

if (lock_state.discarded_size > 0) {
  // The kernel discarded the VMO. Re-initialize it if required.
  zx_vmo_write(vmo, data, 0, sizeof(data));
} else {
  // The kernel did not discard the VMO. Previous contents were preserved.
}

記憶體壓力信號

Fuchsia 提供使用者空間處理程序,可直接控管 記憶體用量 (以回應整個系統的可用記憶體)用戶端可以 註冊即可接收 記憶體壓力 信號 並根據觀察到的記憶體壓力等級採取行動另有 三種記憶體壓力等級

名稱說明
1

記憶體壓力水平良好。

已註冊的用戶端可自由保留快取及分配記憶體 無限制

不過,用戶端應小心不要主動重新建立 過渡到 NORMAL 層級 導致記憶體立即激增 將層級再次推送到 WARNING。

2

記憶體壓力等級有點受限,而且可能會跨越 減少的關鍵壓力範圍

已註冊的用戶端應將作業最佳化以限制記憶體 而非達到最佳效能,例如縮減快取大小 以及非必要的記憶體配置

客戶必須謹慎控管工作量 才能取回記憶體,並確保系統不會顯示 效能會降低存在一些記憶體壓力,但數量不足 因為這類遊戲可以彌補使用者回應的不足,讓他們收回記憶體。

3

記憶體壓力等級非常有限

已註冊的用戶端應會捨棄所有非必要記憶體, 分配更多記憶體未確實執行可能會導致工作 或系統重新啟動 (如為全球使用者) 記憶體壓力

必要時,用戶端可能會耗費大量心力來收回記憶體, 否則頻道可能會遭到終止。客戶可能會判斷 在此例中,效能命中是公平的取捨。

比較記憶體壓力訊號和可捨棄的 VMO

使用者空間用戶端可以選擇記憶體壓力訊號或可捨棄 VMO,或根據其 做出選擇時,請將下列事項納入考量:

  • 記憶體壓力信號可讓用戶端做的不只是裁剪快取。 例如,工作可以拆解工作樹狀結構中的非必要程序。 此外,他們也可以停止會佔用大量記憶體的活動, 再開始新的運動,直到壓力水平變為「正常」為止
  • 若是可捨棄的 VMO,使用者空間用戶端能掌控 將記憶體釋出給核心核心會決定何時釋放 決定記憶體容量 可由其他方式取回。如果客戶想要 控制其快取的生命週期、何時該修剪內容等等 壓力信號可能更合適。
  • 可捨棄的 VMO 最終可能會保留內容 時間超過 25% 而非記憶體壓力信號核心是沒有可捨棄的 VMO 核心對於可用記憶體容量有更全面的瞭解 才知道該回收多少錢核心也有其他方法 因此並非所有可能性 才能釋出可捨棄的 VMO。另一方面, 用戶端就會回應記憶體壓力本身 每次都採用相同方式來修剪所有的快取
  • 可捨棄的記憶體也可讓核心收回更多記憶體 藉此加快系統復原速度透過記憶體壓力信號 核心之間可能存在一些處理序間通訊 (IPC) 和排程延遲時間 表示壓力程度變化,以及使用者空間程序有所因應

OOM (記憶體不足) 重新啟動

所有記憶體重新宣告策略都有可能無法順利釋出空間 會面臨某些激增的記憶體配置模式在此之後 發生這類情況時,核心選擇在徹底關閉檔案系統後重新啟動, 以防止資料遺失可用記憶體等級低於預先設定的 OOM 門檻時,即會觸發 OOM 重新啟動。

測試記憶體壓力反應的工具

觀察及測試核心記憶體收回

使用 k scanner 指令來觀察及測試重述技巧 核心用途:分頁器支援撤銷、可捨棄的 VMO 回送、零頁 以及頁面表格重寫這項功能也可用來測試 用於推動清除作業的佇列輪替 / 老化策略。在以下裝置上執行 k scanner: 查看所有可用選項:

k scanner
usage:
scanner dump                    : dump scanner info
scanner push_disable            : increase scanner disable count
scanner pop_disable             : decrease scanner disable count
scanner reclaim_all             : attempt to reclaim all possible memory
scanner rotate_queue            : immediately rotate the page queues
scanner reclaim <MB> [only_old] : attempt to reclaim requested MB of memory.
scanner pt_reclaim [on|off]     : turn unused page table reclamation on or off
scanner harvest_accessed        : harvest all page accessed information

k scanner dump 會傾印網頁佇列的目前狀態和其他相關 核心用於重組的記憶體計數器:

k scanner dump
[SCAN]: Scanner enabled. Triggering informational scan
[SCAN]: Found 4303 zero pages across all of memory
[SCAN]: Found 8995 user-pager backed pages in queue 0
[SCAN]: Found 3278 user-pager backed pages in queue 1
[SCAN]: Found 8947 user-pager backed pages in queue 2
[SCAN]: Found 10776 user-pager backed pages in queue 3
[SCAN]: Found 3981 user-pager backed pages in queue 4
[SCAN]: Found 0 user-pager backed pages in queue 5
[SCAN]: Found 0 user-pager backed pages in queue 6
[SCAN]: Found 0 user-pager backed pages in queue 7
[SCAN]: Found 1347 user-pager backed pages in DontNeed queue
[SCAN]: Found 40 zero forked pages
[SCAN]: Found 0 locked pages in discardable vmos
[SCAN]: Found 0 unlocked pages in discardable vmos
pq: MRU generation is 12 set 10.720698018s ago due to "Active ratio", LRU generation is 6
pq: Pager buckets [8995],[3278],8947,10776,3981,0,{0},0, evict first: 1347, live active/inactive totals: 12273/25051

使用 k scanner reclaimk scanner reclaim_all 測試收回記憶體:

k scanner reclaim_all
[EVICT]: Free memory before eviction was 7161MB and after eviction is 7290MB
[EVICT]: Evicted 33004 user pager backed pages
[SCAN]: De-duped 25 pages that were recently forked from the zero page

使用 k pmm drop_user_pt 測試頁面資料表宣告:

k pmm
…
pmm drop_user_pt                             : drop all user hardware page tables

觀察並產生記憶體壓力

使用 k mem avail_state 指令在 讓系統分配記憶體來達到指定的記憶體壓力等級這個 可用於測試整個系統對記憶體壓力的回應:

k mem avail_state
mem avail_state [step] <state> [<nsecs>] : allocate memory to go to memstate <state>, hold the state for <nsecs> (10s by default). Only works if going to <state> from current state requires allocating memory, can't free up pre-allocated memory. In optional [step] mode, allocation pauses for 1 second at each intermediate memory availability state until <state> is reached.

k mem dump 會轉儲目前的記憶體壓力狀態。

k mem dump
watermarks: [50M, 60M, 150M, 300M]
debounce: 1M
current state: 4
current bounds: [299M, 16.0E]
free memory: 7253.5M

記憶體可用性狀態是從 0 開始編號,且為超集 前述的記憶體壓力等級 信號

  • OOM 為狀態 0。這是核心低於這個數值的可用記憶體級別 並決定重新啟動系統。
  • Imminent-OOM 為狀態 1。這是僅供診斷的記憶體層級, 和 OOM 等級的細微差異這麼做的唯一目的是 以便安全地收集 OOM 診斷資訊,因為可能太晚 收集自 OOM 層級的診斷資料如要進一步瞭解這個等級,請前往 RFC-0091
  • Critical 為狀態 2。觸發極端動作的關卡 記憶體壓力信號
  • Warning 為狀態 3。這是觸發 WARNING 記憶體的層級 壓力信號。
  • Normal 為狀態 4。這是會觸發「NORMAL」記憶體的層級 壓力信號。

在上述範例中,current state 為 4,也就是 Normal。

watermarks 會顯示區分不同記憶體的記憶體門檻 可用性狀態。上述範例的輸出內容顯示 門檻:

OOM: 50MB, Imminent-OOM: 60MB, Critical: 150MB, Warning: 300MB

debounce 是計算記憶體狀態時使用的切片或錯誤邊界 界定範圍本例中的檔案大小為 1 MB。

current bounds 會顯示目前適用的可用記憶體邊界 記憶體狀態假設目前狀態為 Normal,請參考 watermarksNormal 從 300 MB 的門檻開始計算。如果使用 1MB Debounce 大小下限為 299 MB「Normal」未設定適用的上限 層級,這裡已設為 UINT64_MAX

最後,系統中的 free memory 總量為 7253.5 MB。

使用 k mem avail_state X 指令轉換為記憶體可用性 X 狀態,其中 X 是上述的數值記憶體狀態。 您可以選擇提供保留要求狀態的持續時間,有 也可選擇「步驟」進入中間狀態,在每個狀態的 具體做法是指示 Kubernetes 建立並維護 一或多個代表這些 Pod 的物件

舉例來說,這會觸發轉換至 Critical 記憶體狀態的範例:

k mem avail_state 2
memory-pressure: memory availability state - Critical
pq: MRU generation is 714 set 4.144414945s ago due to "Active ratio", LRU generation is 708
pq: Pager buckets [3482],[115],317,0,199,0,{6939},0, evict first: 0, live active/inactive totals: 3597/7455
memory-pressure: set target memory to evict 1MB (free memory is 149MB)
Leaked 1817528 pages
Sleeping for 10 seconds...
[EVICT]: Free memory before eviction was 147MB and after eviction is 151MB
[EVICT]: Evicted 986 user pager backed pages
Freed 1817528 pages
memory-pressure: memory availability state - Normal
pq: MRU generation is 717 set 1.213355379s ago due to "Timeout", LRU generation is 711
pq: Pager buckets [4351],[258],149,37,0,1,{5798},0, evict first: 0, live active/inactive totals: 4609/5985

為此,系統已分配 1817528 個網頁,轉換為 Critical ( 頁面大小為 4 KB)。後來發現睡眠時間達 10 秒 (預設為按住 狀態) 持續存在 Critical 壓力。最後,1817528 年 已釋出分配的頁面,並將記憶體壓力降回 NormalCritical 狀態轉換導致部分受分頁器支援的記憶體遭到撤銷, ,且可透過 [EVICT] 行查看。

k mem avail_state 指令是測試記憶體壓力的實用工具 我們會全面回應系統因為它的運作原理 就會執行系統本身擁有的所有重組機制 同時在核心和使用者空間中處置。

這些是額外的 k mem oom 指令,用於測試系統回應 尤其是在 OOM 層級

mem oom [<rate>]                             : leak memory until oom is triggered, optionally specify the rate at which to leak (in MB per second)
mem oom hard                                 : leak memory aggressively and keep on leaking
mem oom signal                               : trigger oom signal without leaking memory

使用 k mem oom 的輸出內容範例:

k mem oom
Disabling VM scanner
memory-pressure: free memory is 49MB, evicting pages to prevent OOM...
pq: MRU generation is 13 set 7.979442243s ago due to "Active ratio", LRU generation is 7
pq: Pager buckets [4538],[4517],3624,4606,13716,4976,{0},0, evict first: 1347, live active/inactive totals: 9055/28269
memory-pressure: found no pages to evict
memory-pressure: free memory after OOM eviction is 49MB
…
memory-pressure: pausing for 8s after OOM mem signal
[00028.317] 02811:03481> [fshost] INFO: [admin-server.cc(33)] received shutdown command over admin interface
[00028.317] 02811:03481> [fshost] INFO: [fs-manager.cc(281)] filesystem shutdown initiated
[00028.317] 02811:38032> [fshost] INFO: [fs-manager.cc(310)] Shutting down /data
[00028.318] 12900:12902> [minfs] INFO: [minfs.cc(1471)] Shutting down
[00028.340] 12900:12902> [minfs] WARNING: [src/storage/minfs/bin/main.cc(53)] Unmounted
[00028.341] 02811:03481> [fshost] INFO: [admin-server.cc(39)] shutdown complete
[00028.342] 02811:02813> [fshost] INFO: [main.cc(309)] terminating
[00028.342] 02687:02689> [driver_manager.cm] INFO: [suspend_handler.cc(205)] Successfully waited for VFS exit completion
memory-pressure: rebooting due to OOM
memory-pressure: stowing crashlog
ZIRCON REBOOT REASON (OOM)
Shutting down debuglog
platform_halt suggested_action 1 reason 3
Rebooting...

在使用者空間中模擬記憶體壓力信號

使用 ffx profile memory signal 指令模擬以下項目的記憶體壓力信號: 而不會造成實際的記憶體壓力如果目標是達到 測試特定使用者空間處理程序對記憶體壓力的回應 且不會改變系統的記憶體狀態

Signals userspace clients with specified memory pressure level. Clients can use this
command to test their response to memory pressure. Does not affect the real memory
pressure level on the system, or trigger any kernel reclamation tasks.
Positional Arguments:
  level             memory pressure level. Can be CRITICAL, WARNING or NORMAL.

例如,使用 ffx profile memory signal WARNING 時,ffx log 輸出內容中會顯示以下內容:

[00213.059579][26701][26703][memory_monitor] INFO: [pressure_notifier.cc:106] Simulating memory pressure level WARNING

請注意,這個指令實際上不會分配任何記憶體。這只是 它會模擬所請求層級的一次性記憶體壓力信號 而不影響核心記憶體可用性狀態。因此 不會觸發任何核心記憶體收回設定,例如撤銷頁面伺服器支援的呼叫 記憶體用量