android 進程間的內存分配

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,並分爲以下類別:

  • 緩存頁:有存儲器中的文件(例如代碼或內存映射文件)支持的內存。緩存內存有兩種類型:
    • 私有頁:由一個進程擁有且未共享
      • 乾淨頁:存儲器中未經修改的文件副本,可由 kswapd 刪除以增加可用內存
      • 髒頁:存儲器中經過修改的文件副本;可由 kswapd 移動到 zRAM 或在 zRAM 中進行壓縮以增加可用內存
    • 共享頁:由多個進程使用
      • 乾淨頁:存儲器中未經修改的文件副本,可由 kswapd 刪除以增加可用內存
      • 髒頁:存儲器中經過修改的文件副本;允許通過 kswapd 或者通過明確使用 msync()munmap() 將更改寫回存儲器中的文件,以增加可用空間
  • 匿名頁:沒有存儲器中的文件支持的內存(例如,由設置了 MAP_ANONYMOUS 標記的 mmap() 進行分配)
    • 髒頁:可由 kswapd 移動到 zRAM/在 zRAM 中進行壓縮以增加可用內存

注意:乾淨頁包含存在於存儲器中的文件(或文件一部分)的精確副本。如果幹淨頁不再包含文件的精確副本(例如,因應用操作所致),則會變成髒頁。乾淨頁可以刪除,因爲始終可以使用存儲器中的數據重新生成它們;髒頁則不能刪除,否則數據將會丟失。

隨着系統積極管理 RAM,可用和已使用頁面的比例會不斷變化。本部分介紹的概念對於管理內存不足的情況至關重要。本文檔的下一部分將對這些概念進行更詳細的說明。

內存不足管理

Android 有兩種處理內存不足情況的主要機制:內核交換守護進程和低內存終止守護進程。

內核交換守護進程

內核交換守護進程 (kswapd) 是 Linux 內核的一部分,用於將已使用內存轉換爲可用內存。當設備上的可用內存不足時,該守護進程將變爲活動狀態。Linux 內核設有可用內存上下限閾值。當可用內存降至下限閾值以下時,kswapd 開始回收內存。當可用內存達到上限閾值時,kswapd 停止回收內存。

kswapd 可以刪除乾淨頁來回收它們,因爲這些頁受到存儲器的支持且未經修改。如果某個進程嘗試處理已刪除的乾淨頁,則系統會將該頁面從存儲器複製到 RAM。此操作稱爲“請求分頁”。

由存儲器支持的乾淨頁已刪除

圖 2. 由存儲器支持的乾淨頁已刪除

kswapd 可以將緩存的私有髒頁和匿名髒頁移動到 zRAM 進行壓縮。這樣可以釋放 RAM 中的可用內存(可用頁面)。如果某個進程嘗試處理 zRAM 中的髒頁,該頁將被解壓縮並移回到 RAM。如果與壓縮頁面關聯的進程被終止,則該頁面將從 zRAM 中刪除。

如果可用內存量低於特定閾值,系統會開始終止進程。

髒頁被移至 zRAM 並進行壓縮

圖 3. 髒頁被移至 zRAM 並進行壓縮

低內存終止守護進程

很多時候,kswapd 不能爲系統釋放足夠的內存。在這種情況下,系統會使用 onTrimMemory() 通知應用內存不足,應該減少其分配量。如果這還不夠,內核會開始終止進程以釋放內存。它會使用低內存終止守護進程 (LMK) 來執行此操作。

LMK 使用一個名爲 oom_adj_score 的“內存不足”分值來確定正在運行的進程的優先級,以此決定要終止的進程。最高得分的進程最先被終止。後臺應用最先被終止,系統進程最後被終止。下表列出了從高到低的 LMK 評分類別。評分最高的類別,即第一行中的項目將最先被終止:

Android 進程,高分在上

圖 4. Android 進程,高分在上,低分在下

以下是上表中各種類別的說明:

  • 後臺應用:之前運行過且當前不處於活動狀態的應用。LMK 將首先從具有最高 oom_adj_score 的應用開始終止後臺應用。

  • 上一個應用:最近用過的後臺應用。上一個應用比後臺應用具有更高的優先級(得分更低),因爲相比某個後臺應用,用戶更有可能切換到上一個應用。

  • 主屏幕應用:這是啓動器應用。終止該應用會使壁紙消失。

  • 服務:服務由應用啓動,可能包括同步或上傳到雲端。

  • 可覺察的應用:用戶可通過某種方式察覺到的非前臺應用,例如運行一個顯示小界面的搜索進程或聽音樂。

  • 前臺應用:當前正在使用的應用。終止前臺應用看起來就像是應用崩潰了,可能會向用戶提示設備出了問題。

  • 持久性(服務):這些是設備的核心服務,例如電話和 WLAN。

  • 系統:系統進程。這些進程被終止後,手機可能看起來即將重新啓動。

  • 原生:系統使用的極低級別的進程(例如,kswapd)。

設備製造商可以更改 LMK 的行爲。

計算內存佔用量

內核會跟蹤系統中的所有內存頁面。

不同進程使用的頁面

圖 5. 不同進程使用的頁面

在確定應用使用的內存量時,系統必須考慮共享的頁面。訪問相同服務或庫的應用將共享內存頁面。例如,Google Play 服務和某個遊戲應用可能會共享位置信息服務。這樣便很難確定屬於整個服務和每個應用的內存量分別是多少。

由兩個應用共享的頁面

圖 6. 由兩個應用共享的頁面(中間)

如需確定應用的內存佔用量,可以使用以下任一指標:

  • 常駐內存大小 (RSS):應用使用的共享和非共享頁面的數量
  • 按比例分攤的內存大小 (PSS):應用使用的非共享頁面的數量加上共享頁面的均勻分攤數量(例如,如果三個進程共享 3MB,則每個進程的 PSS 爲 1MB)
  • 獨佔內存大小 (USS):應用使用的非共享頁面數量(不包括共享頁面)

如果操作系統想要知道所有進程使用了多少內存,那麼 PSS 非常有用,因爲頁面只會統計一次。計算 PSS 需要花很長時間,因爲系統需要確定共享的頁面以及共享頁面的進程數量。RSS 不區分共享和非共享頁面(因此計算起來更快),更適合跟蹤內存分配量的變化。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章