Android 支持內存為 512 MB 的設備。本文檔旨在幫助 OEM 優化和配置 Android 內核 4.4,使其能夠在低內存設備上運行。在下文所述的優化措施中,有幾項非常通用,甚至也可應用于以前的版本。
Android 內核 4.4 平臺優化
改善了內存管理
采用了經驗證可節省內存的內核配置:交換到 zram。
終止了那些即將被取消緩存且過大的緩存進程。
不允許大型服務自行返回至 A 服務類別(以免導致啟動器終止)。
終止了那些處于空閑維護狀態中的過大進程(甚至終止當前 IME 等通常不可終止的進程)。
對后臺服務的啟動進行了序列化。
優化了低內存設備的內存使用方式:采用更嚴格的內存不足 (OOM) 調整級別、縮減圖形緩存大小。
減少了系統內存占用
刪減 system_server 和系統界面進程(節省了幾兆的內存)。
在 Dalvik 中預加載 dex 緩存(節省了幾兆的內存)。
采用了經驗證的 JIT-off 選項(每個進程最多可節省 1.5MB 的內存)。
減少了各進程的字體緩存開銷。
Procstats
添加了一個開發者選項,以顯示內存狀態和應用內存使用情況(按照運行頻率和所耗內存量排序)。
API
添加了
內存跟蹤
添加了 memtrack HAL 來跟蹤圖形內存分配情況、dumpsys meminfo 中的更多信息,以及 meminfo 中的闡明性總結(例如,所報告的可用內存包括緩存進程占用的內存,這樣 OEM 就不會嘗試優化內存錯誤)。
編譯時配置
低內存設備標記
ActivityManager.isLowRamDevice() 標志可確定應用是否應關閉在低內存設備上表現非常差的某些內存密集型功能。
對于內存為 512 MB 的設備,該標記應返回 true。可以通過在設備 makefile 中使用以下系統屬性來啟用該 API:
PRODUCT_PROPERTY_OVERRIDES += ro.config.low_ram=true
啟動器配置
啟動器的默認壁紙設置不應使用動態壁紙。低內存設備不應預裝任何動態壁紙。
內核配置
優化內核/ActivityManager 以減少直接回收
當進程或內核嘗試分配(直接分配或因新頁面中存在故障而分配)內存頁面并且內核已用盡所有可用內存時,就會發生直接回收。在這種情況下,內核需要釋放一個頁面,并在此過程中阻斷分配操作。而這通常又需要磁盤 I/O 清理一個有文件支持的臟頁或等待 lowmemorykiller 終止一個進程。最終可能會導致任意線程(包括界面線程)中出現額外 I/O。
為避免出現直接回收,內核已配有可觸發 kswapd 或后臺回收的水印。此線程會嘗試釋放頁面,以便下次分配真實線程時,能夠快速順利啟動。
用于觸發后臺回收的默認閾值相當低,在 2GB 設備上約為 2 MB,在 512 MB 設備上約為 636 KB。內核通過后臺回收僅能回收幾兆的內存。這意味著,任何快速分配超過幾兆內容的進程都會快速導致直接回收。
在 Android-3.4 內核分支中,我們通過補丁程序 92189d47f66c67e5fd92eafaa287e153197a454f(“添加用于擴展可用內存空間的可調選項”)添加了對內核可調選項的支持。如果您選擇將該補丁程序添加到設備內核中,ActivityManager 會告知內核嘗試保留能容納 3 個全屏 32 bpp 緩沖區的可用內存空間。
這些閾值可通過 config.xml 框架進行配置。
-1
0
優化 LowMemoryKiller
ActivityManager 可配置 LowMemoryKiller 的閾值,使其符合它對在每個優先級分段中運行進程時所需的文件支持頁面(緩存頁面)工作集的預期。如果設備對工作集有很高的要求(例如:如果供應商界面需要更多內存,或者如果添加了更多服務),則可增大閾值。
如果為文件支持頁面預留了太多內存,則可減小閾值,以便系統能夠在因緩存變得過小而導致磁盤超負荷之前就終止后臺進程。
-1
0
交換到 zram
zram 交換可通過壓縮內存頁面并將其放入動態分配的內存交換區來增加系統中的可用內存量。由于這是以犧牲 CPU 時間為代價來增加少量內存,因此您應仔細權衡 zram 交換會對您系統的性能造成的負面影響。
Android 會在多個層面上處理 zram 交換:
首先,必須啟用以下內核選項,才能有效地使用 zram 交換:
CONFIG_SWAP
CONFIG_ZRAM
然后,您應將一行與下列類似的內容添加到 fstab 中:/dev/block/zram0 none swap defaults zramsize=,swapprio=
zramsize 是必要內容,表示您希望 zram 區域占用多少未壓縮內存。壓縮比通常介于 30-50% 之間。
僅當您沒有多個交換區時才需要 swapprio。
在設備專用 swap_block_device,以便 SELinux 適當地對其進行處理。
/dev/block/zram0 u:object_r:swap_block_device:s0
默認情況下,Linux 內核每次會換入 8 頁內存。當使用 zram 時,每次讀取 1 頁內存產生的增量開銷微乎其微,如果設備承受巨大的內存壓力,可能有所助益。要想每次只讀取 1 頁內存,請將以下內容添加到您的 init.rc 中:write /proc/sys/vm/page-cluster 0
在 init.rc 行的 mount_all /fstab.X 行之后,添加:swapon_all /fstab.X
如果在內核中啟用了此功能,系統便會在啟動時自動配置內存 cgroups。
如果內存 cgroup 可用,則 ActivityManager 將優先級較低的線程標記為可比其他線程更適合交換。在需要內存時,Android 內核會開始將內存頁面遷移到 zRAM 交換區,并會優先處理那些已被 ActivityManager 標記的內存頁面。
Carveout、Ion 和連續內存分配 (CMA)
在低內存設備上,請務必注意 carveout,尤其是那些未得到充分利用的 carveout,例如用于安全地播放視頻的 carveout。有幾種解決方案可最大限度地減小 carveout 區域的影響,具體取決于硬件的確切要求。
如果硬件允許不連續的內存分配,則可利用 Ion 系統堆從系統內存中分配內存,這樣便無需使用 carveout。Ion 還會嘗試增大分配的內存空間以消除外圍設備上的轉譯后備緩沖區 (TLB) 壓力。如果內存區域必須連續或必須限定在某個特定地址范圍內,則可以使用 CMA。
這將創建一個 carveout,系統也可以將其用于處理可移動頁面。
當需要該區域時,可移動頁面就會從中移出,以便系統將處于空閑狀態的大型 carveout 用于其他目的。 您可以直接將 CMA 與 Ion CMA 堆配合使用。
應用優化提示
使用 development/tools/findunused 移除預安裝應用中所有未使用的資源(這應該會有助于減小應用所占用的空間)。
針對資源(特別是具有透明區域的資源)使用 PNG 格式。
編寫原生代碼時,請使用 calloc() 而非 malloc/memset。
請勿啟用會將 Parcel 數據寫入磁盤并在之后讀取這些數據的代碼。
使用 SSP 過濾,而不要訂閱已安裝的所有軟件包。請添加如下所示的過濾條件:
了解 Android 中的各種進程狀態
狀態
含義
詳情
SERVICE
SERVICE_RESTARTING
由于與應用相關的原因而在后臺運行的應用。
SERVICESERVICE_RESTARTING 是應用過于頻繁地在后臺運行時會遇到的最常見的問題。使用 %duration * pss 或 %duration 作為“不良”指標。理想情況下,這些應用根本不應該運行。
IMPORTANT_FOREGROUND
RECEIVER
在后臺運行的應用(不直接與用戶交互)。
這些應用會增加系統的內存負載。使用 (%duration * pss)“不良”值來對這些進程進行排序。不過,很多此類應用都會因合理原因而需要運行。pss 大小將會是它們的內存負載的重要組成部分。
PERSISTENT
持續的系統進程。
跟蹤 pss 可監視這些進程是否會變得過大。
TOP
正與用戶交互的進程。
pss 在此又成為了重要指標,可顯示應用在使用過程中產生的內存負載。
HOME
CACHED_EMPTY
系統保留的備用進程。
這些進程可隨時終止,并可根據需要重新創建。內存狀態(“正常”、“中等”、“低”、“嚴重”)是根據系統運行的此類進程的數量進行計算的。這些進程的關鍵指標為 pss。在這種狀態下,這些進程應盡可能地減少其內存占用空間,以便系統能夠保留盡可能多的進程。一般來說,與在 TOP 狀態下相比,運行狀況良好的應用在該狀態下的 pss 占用空間明顯更小。
CACHED_ACTIVITY
CACHED_ACTIVITY_CLIENT
與 TOP 相比,這些狀態顯示了應用將內存釋放到后臺的程度。
排除 CACHED_EMPTY 狀態可改善這些數據,因為這項操作會排除因某些原因(除了與用戶互動之外)而啟動進程的情況。
這樣便無需處理 CACHED_EMPTY 在執行與用戶相關的 Activity 時產生的界面開銷。
分析
分析應用啟動時間
如需分析應用的啟動時間,請運行 $ adb shell am start -P 或 --start-profiler 并啟動您的應用。在該進程從 zygote 分 k 之后以及任何代碼加載到該分支之前,分析器就會啟動。
使用錯誤報告進行分析
錯誤報告包含一些服務(包括 batterystats、netstats、procstats、usagestats),它們可用于調試。報告可包括以下行:
------ CHECKIN BATTERYSTATS (dumpsys batterystats --checkin) ------
7,0,h,-2558644,97,1946288161,3,2,0,340,4183
7,0,h,-2553041,97,1946288161,3,2,0,340,4183
檢查是否存在任何持續進程
要檢查任何持續進程,請重新啟動設備并檢查進程情況。然后,讓設備運行幾個小時,然后再次檢查進程情況。
兩次檢查之間不應存在任何長時間運行的進程。
運行長時測試
要運行長時測試,請讓設備運行較長時間,并跟蹤進程的內存占用情況,以確定內存用量增加還是保持不變。然后擬訂規范的使用情形,并針對這些情形運行長時測試。