https://developer.android.google.cn/topic/performance/memory-management
Android 平臺在運行時不會浪費可用的內存。它會一直嘗試利用所有可用內存。例如,系統會在應用關閉後將其保留在內存中,以便用戶快速切回到這些應用。因此,通常情況下,Android 設備在運行時幾乎沒有可用的內存。要在重要系統進程和許多用戶應用之間正確分配內存,內存管理至關重要。
本頁討論了 Android 如何爲系統和用戶應用分配內存的基礎知識,另外還說明了操作系統如何應對低內存情況。
內存類型
Android 設備包含三種不同類型的內存:RAM、zRAM 和存儲器。請注意,CPU 和 GPU 訪問同一個 RAM。
圖 1. 內存類型 - RAM、zRAM 和存儲器
-
RAM 是最快的內存類型,但其大小通常有限。高端設備通常具有最大的 RAM 容量。
-
zRAM 是用於交換空間的 RAM 分區。所有數據在放入 zRAM 時都會進行壓縮,然後在從 zRAM 向外複製時進行解壓縮。這部分 RAM 會隨着頁面進出 zRAM 而增大或縮小。設備製造商可以設置 zRAM 大小上限。
-
存儲器中包含所有持久性數據(例如文件系統等),以及爲所有應用、庫和平臺添加的對象代碼。存儲器比另外兩種內存的容量大得多。在 Android 上,存儲器不像在其他 Linux 實現上那樣用於交換空間,因爲頻繁寫入會導致這種內存出現損壞,並縮短存儲媒介的使用壽命。
內存頁面
RAM 分爲多個“頁面”。通常,每個頁面爲 4KB 的內存。
系統會將頁面視爲“可用”或“已使用”。可用頁面是未使用的 RAM。已使用的頁面是系統目前正在使用的 RAM,並分爲以下類別:
- 緩存頁:有存儲器中的文件(例如代碼或內存映射文件)支持的內存。緩存內存有兩種類型:
- 匿名頁:沒有存儲器中的文件支持的內存(例如,由設置了
MAP_ANONYMOUS
標記的mmap()
進行分配)- 髒頁:可由
kswapd
移動到 zRAM/在 zRAM 中進行壓縮以增加可用內存
- 髒頁:可由
注意:乾淨頁包含存在於存儲器中的文件(或文件一部分)的精確副本。如果幹淨頁不再包含文件的精確副本(例如,因應用操作所致),則會變成髒頁。乾淨頁可以刪除,因爲始終可以使用存儲器中的數據重新生成它們;髒頁則不能刪除,否則數據將會丟失。
隨着系統積極管理 RAM,可用和已使用頁面的比例會不斷變化。本部分介紹的概念對於管理內存不足的情況至關重要。本文檔的下一部分將對這些概念進行更詳細的說明。
內存不足管理
Android 有兩種處理內存不足情況的主要機制:內核交換守護進程和低內存終止守護進程。
內核交換守護進程
內核交換守護進程 (kswapd
) 是 Linux 內核的一部分,用於將已使用內存轉換爲可用內存。當設備上的可用內存不足時,該守護進程將變爲活動狀態。Linux 內核設有可用內存上下限閾值。當可用內存降至下限閾值以下時,kswapd
開始回收內存。當可用內存達到上限閾值時,kswapd
停止回收內存。
kswapd
可以刪除乾淨頁來回收它們,因爲這些頁受到存儲器的支持且未經修改。如果某個進程嘗試處理已刪除的乾淨頁,則系統會將該頁面從存儲器複製到 RAM。此操作稱爲“請求分頁”。
圖 2. 由存儲器支持的乾淨頁已刪除
kswapd
可以將緩存的私有髒頁和匿名髒頁移動到 zRAM 進行壓縮。這樣可以釋放 RAM 中的可用內存(可用頁面)。如果某個進程嘗試處理 zRAM 中的髒頁,該頁將被解壓縮並移回到 RAM。如果與壓縮頁面關聯的進程被終止,則該頁面將從 zRAM 中刪除。
如果可用內存量低於特定閾值,系統會開始終止進程。
圖 3. 髒頁被移至 zRAM 並進行壓縮
低內存終止守護進程
很多時候,kswapd
不能爲系統釋放足夠的內存。在這種情況下,系統會使用 onTrimMemory()
通知應用內存不足,應該減少其分配量。如果這還不夠,內核會開始終止進程以釋放內存。它會使用低內存終止守護進程 (LMK) 來執行此操作。
LMK 使用一個名爲 oom_adj_score
的“內存不足”分值來確定正在運行的進程的優先級,以此決定要終止的進程。最高得分的進程最先被終止。後臺應用最先被終止,系統進程最後被終止。下表列出了從高到低的 LMK 評分類別。評分最高的類別,即第一行中的項目將最先被終止:
圖 4. Android 進程,高分在上,低分在下
以下是上表中各種類別的說明:
-
後臺應用:之前運行過且當前不處於活動狀態的應用。LMK 將首先從具有最高
oom_adj_score
的應用開始終止後臺應用。 -
上一個應用:最近用過的後臺應用。上一個應用比後臺應用具有更高的優先級(得分更低),因爲相比某個後臺應用,用戶更有可能切換到上一個應用。
-
主屏幕應用:這是啓動器應用。終止該應用會使壁紙消失。
-
服務:服務由應用啓動,可能包括同步或上傳到雲端。
-
可覺察的應用:用戶可通過某種方式察覺到的非前臺應用,例如運行一個顯示小界面的搜索進程或聽音樂。
-
前臺應用:當前正在使用的應用。終止前臺應用看起來就像是應用崩潰了,可能會向用戶提示設備出了問題。
-
持久性(服務):這些是設備的核心服務,例如電話和 WLAN。
-
系統:系統進程。這些進程被終止後,手機可能看起來即將重新啓動。
-
原生:系統使用的極低級別的進程(例如,
kswapd
)。
設備製造商可以更改 LMK 的行爲。
計算內存佔用量
內核會跟蹤系統中的所有內存頁面。
圖 5. 不同進程使用的頁面
在確定應用使用的內存量時,系統必須考慮共享的頁面。訪問相同服務或庫的應用將共享內存頁面。例如,Google Play 服務和某個遊戲應用可能會共享位置信息服務。這樣便很難確定屬於整個服務和每個應用的內存量分別是多少。
圖 6. 由兩個應用共享的頁面(中間)
如需確定應用的內存佔用量,可以使用以下任一指標:
- 常駐內存大小 (RSS):應用使用的共享和非共享頁面的數量
- 按比例分攤的內存大小 (PSS):應用使用的非共享頁面的數量加上共享頁面的均勻分攤數量(例如,如果三個進程共享 3MB,則每個進程的 PSS 爲 1MB)
- 獨佔內存大小 (USS):應用使用的非共享頁面數量(不包括共享頁面)
如果操作系統想要知道所有進程使用了多少內存,那麼 PSS 非常有用,因爲頁面只會統計一次。計算 PSS 需要花很長時間,因爲系統需要確定共享的頁面以及共享頁面的進程數量。RSS 不區分共享和非共享頁面(因此計算起來更快),更適合跟蹤內存分配量的變化。