Android app memory 基本分析

Joseph Cheng
KKday Tech Blog
Published in
8 min readJul 22, 2018

Android 的 memory 分配,基本上可以分為下面這幾種

  • Java

Java heap。Java/Kotlin code 裡面 object 實際會分配到的地方,garbage collection 主要收集回收的地方

  • Native

Native heap。c / c++ code 裡面 object 實際會分配到的地方,如果你用使用 JNI,那些 .so 檔會用到的 memory 的地方,皆屬此類

  • Graphics

用於 render 用的 buffer 空間

  • Stack

App 裡面 Java / Native stack 會用到的空間

  • Code

code 跟 resources, 像是 dex bytecode, optimized or compiled dex code, .so 檔, 以及 字型檔

  • Other

App 裡面系統不知道怎麼分類的,屬於此類

  • Allocated

Java / Kotlin 已分配的 object 的數量

另外 Bitmap 存放的位置比較特別,Bitmap 資料分成兩種,一個是基本資料 (像是寬度)、另一個是 pixel 的資料。基本資料都是存在 java heap。而 pixel 的儲存位置則是因 Android 版本而異

官方文件有提到:

On Android 2.3.3 (API level 10) and lower, the backing pixel data for a bitmap is stored in native memory. It is separate from the bitmap itself, which is stored in the Dalvik heap. The pixel data in native memory is not released in a predictable manner, potentially causing an application to briefly exceed its memory limits and crash. From Android 3.0 (API level 11) through Android 7.1 (API level 25), the pixel data is stored on the Dalvik heap along with the associated bitmap. In Android 8.0 (API level 26), and higher, the bitmap pixel data is stored in the native heap.

在 Android 2.3.3 以前 pixel 資料是存放在 native memory。
在 Android 3.0 ~ 7.x 之間 pixel 資料是存放在 java heap
在 Android 8.0 以後,pixel 資料是存放在 native heap

通常跟一般開發者會有相關的是 Java heap 的部分,因為這個是 garbage collection 主要收集回收的地方,一但 app 這個部分 memory 超過使用限制的話,系統就會發出 out of memory exception (OOM), app 就因此閃退,(如果你的資料是存在 native heap 的話,就不會有單一 app memory 的限制) 因此,我們要先了解一下,到底我們 memory 用量要超過多少,才會發生 OOM

每個裝置的單一 app 的 memory 最大用量的限制

adb shell getprop | grep heapgrowthlimit
adb shell getprop | grep heapsize

heapgrowthlimit 是一般情況下的 heap size
heapsize 是使用 largeHeap 後,可以用的最大 heap size

以我手上的 pixel 2來看

[dalvik.vm.heapgrowthlimit]: [192m]
[dalvik.vm.heapsize]: [512m]

這個最大用量的限制,一般是因手機、Android 系統而異,Google有規定出廠的手機,Android 系統最低 memory 應該要有多大,這個可以參考這個文件,一般來說手機廠商都會依循 Google 規定,不會另外調整

另外我們常常看到手機廠商常常廣告說,自己的手機內建 x GB 的 memory,但是如果手機限制單一 app 最大的 memory 使用量的話,app 一但結果這個限制,一樣會發生 OOM 閃退,跟手機內建的 memory 不是成正比。

一般情況底下,我們會發覺 android memory 使用量過大,通常不外乎 1. 圖片量用量過大 2. memory leak

如同先前所述 Bitmap 在 Android 8.0 以後是存放在 native heap 裡面,所以不會有單一 app memory 的限制,但是在 Android 7.0 以前就有這個問題

處理圖片的大小

很多時候,我們手機就是那麼小,就算給你超高解析度的圖片,也根本就顯示不出來,徒增 memory 的用量。

而且手機的前置相機拍出來的照片大小,遠大於手機螢幕的解析度,以我手上的 pixel 2 為例,他的前置相機的畫素為12.2 MP,也就是 12,200,000 pixels,而我的螢幕解析度為 1080 * 1920 pixels,也就是 2,073,600 pixels,有很大一部分比例,其實手機根本顯示不出來。

  • 那這些圖片到底會佔了多少 memory?

一個 pixel 最大的顯示規格為 RGBA8888 (R: red, G: green, B: blue, A: alpha) 共佔 32 bit,也就是 4 bytes,以我手上的 pixel 2 為例,他的解析度為 1080 * 1920 pixels。因此一張 full screen 的圖片,在最好的情況底下,他會佔用 1080 *1920 *4 ~= 4 MB的 memory。

  • 那我們到底要多少大小的圖片?

通常設計稿都會標記所需圖片的 dp 大小,1 dp 對應 ldpi, mdpi, xhdpi, xxhdpi, xxxhdpi 螢幕為 0.75 px, 1px, 1.5px, 2px, 3px, 4px,因此我們可以輕易換算出所需圖片的 pixel 大小

如果你的圖片是從 server 來的,透過這個可以輕易換算到底 server 要給我的大小應該要多少會比較適當,以 40 dp *40 dp 為例,為了相容螢幕 xxxhdpi,server 最小應該給 160 px *160 px的圖片較為適當

但是有人就會問說,為了相容 xxxhdpi,那 xxhdpi, xhdpi, hdpi, mdpi, ldpi 不就犧牲了嗎?

除非 server 有辦法根據你手機的大小,自動回傳相對應的大小給你 (不過通常沒有辦法,因為會有效能、空間、以及顯示速度的問題),為了解決這個問題,最明顯的解決方法就是把拿到的圖片,把他給壓小,再顯示出來,不就得了嗎?但是這個方法的缺點就是為了壓小圖片,你必須花費 CPU 去做壓小的動作,因此圖片顯示的速度上面會慢一些,但是我個人是覺得影響不會那麼大,一般市面上大家常用的第三方圖片顯示 library (Glide, fresco, picasa) 都有內建這個功能,而且他們都有優化壓縮的動作,所以我個人是建議直接用第三方圖片顯示的 library,不用在造輪子了。

另外在圖片上面,大部份的時候,你其實是不需要 RGBA8888 (32 bits), 你可以換成 RGB565 (16 bits),這樣你的圖片大小也可以壓縮一半

處理 memory leak

大部分的 memory leak 都是 context / activity 的洩漏,所以儘量把 context /activity 只放在 Activity / Fragment / Service 端,不要傳遞到別的 object 裡面去。只要一個 activity 洩漏,造成的影響其實是滿可觀的,等於說整個 activity 都會被保留住,如果一個 activity 有用到很多圖片的話,整個花費的memory 是很可觀的,一次的 activity memory leak,造成的影響會非常大

leakcanary 可以有效率的偵測 app 裡面的 memory leak

他基本的原理就是 Activity 跑到 onDestroy 的時候,去檢測 Activity 是否真的被 GC,而且他很方便就是會直接在通知列顯示,是在哪個檔案的那一行所造成的 memory leak,解決完就不會有問題了。

如果還是有 memory 使用過量的問題,更進階的就是要去分析 heap memory 裡面是怎麼使用的 Android Memory Profiler 可以做到這件事,但是分析不是這麼好分析的,因為你只能看到目前已經分配 object 的 class name,沒有辦法看到實際是哪個變數所造成的。另一個進階的去分析 native head 是怎麼使用的,這個也是相當困難的,不過仍然是有人去嘗試分析的,這些方法可能要透過 Google 去查知了。基本上只要解決,圖片的大小跟 memory leak 就不會有問題了

--

--