Android效能優化之記憶體優化

語言: CN / TW / HK

本文正在參加「金石計劃」

Android記憶體優化是一項重要的任務,因為記憶體是Android系統中最關鍵的資源之一。寫出一個app容易,寫好一個app不容易,因為要做大量的優化,並且是持續的優化。關於記憶體優化,我們需要了解哪些知識呢?什麼是記憶體洩漏?如何檢測記憶體洩漏?什麼是OOM記憶體溢位?如何使用Android Studio自帶的記憶體分析工具?帶著這幾個問題我們就進入了正題。

什麼是記憶體洩漏

記憶體洩漏是指在計算機程式中,分配的記憶體空間沒有被正確釋放或回收的情況。這種情況會導致程式佔用越來越多的記憶體,直到最終程式崩潰或系統資源不足。

記憶體洩漏不是我們Android特有的,C/C++語言寫的程式也會記憶體洩漏,我們Java中的記憶體洩漏相對來說比較容易發現和解決。Java的記憶體洩漏發生在強引用中,Java的引用總共有4種,強引用、軟引用、弱引用和虛引用,你宣告變數時不指定引用型別就是強引用。我們知道Java是有一個垃圾回收機制GC的,一個變數被其他類引用,就會增加一個引用計數,只有當這個變數的引用計數降為0時,才會觸發GC的回收。因為當這個變數還被引用的時候,GC是不能回收掉這部分記憶體的,因為可能造成程式錯誤。而記憶體洩漏則發生在一個短週期的類裡面引用了一個長週期的變數,導致了短週期的這個類的引用計數為0本應該被回收,但是拖泥帶水地引用了一個還不能被回收的變數,比如一個耗時的檔案讀寫操作。這個時候就導致了GC也沒有回收這個短週期的類,比如已經被finish的Activity。

如何檢測記憶體洩漏

檢測Android程式的記憶體洩漏其實很簡單,通常我們使用的是LeakCanary。只需要在app模組的build.gradle中新增LeakCanary的依賴即可。 kts // 記憶體洩漏檢測 debugImplementation("com.squareup.leakcanary:leakcanary-android:2.10") 然後我們在打debug包的時候就會附帶安裝一個出詳細記憶體洩漏報告的一個名叫Leaks的app了。當app發生記憶體洩漏的時候,Leaks這個app就會自動幫你記錄記憶體洩漏了。

什麼是記憶體溢位

記憶體溢位是指程式在執行時需要的記憶體超出了系統所能提供的可用記憶體空間,導致程式崩潰或出現異常。在計算機程式中,記憶體通常被用來儲存程式的資料和指令,如果程式需要的記憶體超過了系統所能提供的記憶體空間,就會導致記憶體溢位的問題。

在Android手機系統中,也給每一個應用程序設定了一個能分配的最大記憶體上限。也正是因為這個上限,導致了一些動歪腦筋的應用開發者,給app開多個程序以搶佔更多的記憶體資源。但是,儘管這樣,你程式碼寫的不好,難道你就不記憶體溢位了嗎?答案是否定的,就算把整個手機的記憶體都給你,也防止不了豬隊友寫的垃圾程式碼導致的記憶體溢位。故意寫死迴圈導致程式卡死的這種情況就不是水平問題而上升到人品問題了。

在Android中記憶體溢位通常是由於載入大量Bitmap,而又沒有及時回收導致的。早期的開發者使用軟引用解決OOM問題,而現在這個框架橫生的時代,我們不用自己去解決圖片載入的OOM問題了,我們可以直接使用像Glide這樣的圖片載入框架,它會自動幫我們處理LruCache的問題,當記憶體不夠用的時候,按時間軸最早載入的那部分Bitmap會被釋放掉。

使用meminfo統計記憶體使用情況

adb shell "dumpsys meminfo" 執行這個命令看下你的應用的記憶體使用情況。我們再來看下與程序相關的VSS、RSS、PSS、USS這幾個概念。

  1. VSS(Virtual Set Size):程序保留供其使用的虛擬記憶體量。這包括私有和共享記憶體段。
  2. RSS(Resident Set Size):程序當前使用的實體記憶體量。這包括私有和共享記憶體段。
  3. PSS(Proportional Set Size):程序正在使用的記憶體量,在共享同一記憶體段的所有程序之間按比例共享。該指標通常用於識別多程序環境中程序的實際記憶體使用情況。
  4. USS(Unique Set Size):一個程序獨佔使用的記憶體量,不與任何其他程序共享。這包括私有和共享記憶體段。

翻譯成中文,VSS虛集合大小、RSS常駐集合大小、PSS比例集合大小和USS獨佔集合大小。VSS這個跟我們實際實體記憶體佔用關係並不大,比如一塊記憶體區域被多個變數引用,而實際這個VSS虛擬集合會重複統計,這個就是實體記憶體和程式虛擬記憶體的區別。RSS沒有將共享記憶體大小平分到使用共享記憶體的程序上,由於共享記憶體只會佔用一份記憶體,所以如果你把程序的RSS相加,就會超出很多。而PSS通常被用來衡量應用實際的記憶體使用情況。USS獨立記憶體,統計出來就不會包含多程序共享的那部分記憶體。

使用Android Studio的Profiler對記憶體進行分析

使用Android Studio自帶的記憶體分析工具分析記憶體使用情況,具體開啟方式View > Tool Windows > Profiler

Profiler 選擇MEMORY選項可以看實時記憶體使用情況。然後切到CPU選項,選擇Java/Kotlin Method Trace點選RECORD進行錄製,可以分析該段時間的方法呼叫情況。

總結

記憶體優化主要還是要靠開發中自行避免,在寫的時候就避免記憶體問題是最好的,防範於未然。避免寫出記憶體洩漏的程式碼,比如謹慎使用static關鍵字,常量和工具方法可用。實在是遇到了可能造成記憶體洩漏的程式碼,要轉換成弱引用WeakReference。避免寫出記憶體溢位的程式碼,比如,使用Glide幫你處理圖片載入,儘量不要在大量呼叫的程式碼塊(如迴圈或View的onDraw)裡面建立物件。仔細審查程式碼也能避免記憶體問題的出現,在測試中也能發現記憶體問題,應用上線後在產品運營的分析中也還能發現,但是它們所帶來的結果是不一樣的。在整個環節的越早期處理掉記憶體問題越好,越到後面的環節,付出的代價如時間成本就會越大。