Android性能優化--內存優化

2021-02-13 技術最TOP

本文來自Dotry投稿,連結:https://www.jianshu.com/p/38b627adaecd

上一篇文章關於Android性能優化--啟動優化探討了啟動優化相關的知識點,在本篇將介紹內存優化的相關優化。主要大綱參照如下

2.常見問題

常見的Android內存相關問題,通常可以分為以下三種,內存抖動、內存洩露、內存溢出。

內存抖動:在短時間內有大量的對象被創建或者被回收的現象,主要是循環中大量創建、回收對象。當系統內存不足,不斷GC內存的時候,也有可能出現內存抖動情況。

內存洩露:當一個對象不在使用了,本應該被垃圾回收器回收。但是這個對象由於被其他正在使用的對象所持有,造成無法被回收的現象。

內存溢出:Android系統給每個應用分配的內存也有一個閥值,也就是Heap Size。當應用佔用的內存加上我們申請的內存資源超過了系統分配的最大內存時就會拋出的Out Of Memory異常。

上述三者之間的是一個遞進關係,內存抖動<內存洩露<內存溢出。對於一般應用主要是處理內存抖動和內存洩露兩點,處理好這兩點就會大大降低內存溢出的可能性。

3.內存管理3.1 Java內存管理

JVM的內存回收對於大多數開發者來說接觸的並不是很多。因為JVM本身是有一套內存回收的機制,對於開發者更多的是申請對象直接調用即可,其內部並不是很在意。下面主要通過內存存儲和回收這兩塊介紹。

存儲:JVM將可以存儲內存的空間大概分為棧、本地方法棧、程序計數器、堆、方法區等模塊。

棧:主要是針對方法使用的空間,當JVM在執行方法時,會在此區域中創建一個棧幀來存放方法的各種信息,比如返回值,局部變量表和各種對象引用等。

堆:幾乎所有對象、數組等都是在此分配內存的,在JVM內存中佔的比例也是很大的,也是GC回收的主要陣地,平時我們說的新生代、老年代、永久代也是指這片區域。

方法區:存放類似類定義、常量、編譯後的代碼、靜態變量等。

回收:針對上述各個模塊的內存回收,通常所說的GC主要是對堆空間的回收,一般比較常用的方法為:標記-清除算法、複製算法、分代收集算法等其它方法和其變形。

3.2 Android內存管理

Android 系統主要是在Art和Dalvik虛擬機中的託管環境中跟蹤每個內存分配,當發現有可回收的對象,進行內存回收。回收有兩個目標:在程序中查找將來無法訪問的數據對象,並回收那些對象使用的資源 。

進程間的內存管理:Android對於進程間的內存管理主要是通過內核交換守護程序和onTrimMemory()進程殺死來管理。內核交換守護程序(kswapd):RAM中存在一個區域空間zRAM。當設備上的可用內存不足時,守護程序將變為活動狀態。kswapd可以將緩存的私有髒頁和匿名髒頁移動到zRAM,並在其中進行壓縮。onTrimMemory:系統用於 onTrimMemory()通知應用程式內存即將用盡,並應減少其分配。如果這還不夠,內核將開始殺死進程以釋放內存。它使用低內存殺手(LMK)來執行此操作。PS:LMK 這就會涉及到應用保活等相關。應用內存管理:Android應用內內存管理,主要是從Java層和Native 層優化。本文主要介紹如何從Java層進行內存管理優化,具體細節可以下面會一一介紹。4.常見場景及解決方案4.1 內存抖動

由於短時間內有大量對象進出Young Generiation區導致的,它伴隨著頻繁的GC。

儘量避免在循環體內創建對象,應該把對象創建移到循環體外。注意自定義View的onDraw()方法會被頻繁調用,所以在這裡面不應該頻繁的創建對象。

如下面一部分代碼就對應著內存抖動

    Handler handler = new Handler(){
@Override
public void handleMessage(@NonNull Message msg) {
super.handleMessage(msg);
for (int i =0;i<100;i++){
String string[] = new String[10000];
}
handler.sendEmptyMessageDelayed(1,30);

}
};

通過Profile查看其內存圖

可以看到其內存圖基本上是一個鋸齒狀,是因為這時候一直在創建對象和回收對象所致。

4.2 內存洩露

業內一般對內存洩露的原因總結為長生命周期對象引用短生命周期對象,導致短生命周期對象無法及時回收所致。

1、單例引起的內存洩漏

public static  FacebookAnalysis getInstance(Context context){
if (facebookAnalysis == null){
synchronized (FacebookAnalysis.class){
facebookAnalysis = new FacebookAnalysis(getAppEventsLoggerInstance(context));
}
}
return facebookAnalysis;
}

上面是一個常見的單例模式,如果參數引用Activity的Context,而單例模式的生命周期長於Activity。這裡單例模式引用Activity的實例,當Activity被銷毀,Activity無法被回收,造成內存洩露。如果這裡引用的Application的Context,將無任何影響。因為Application的生命周期與單例模式同樣長。

2、靜態集合添加對象,在使用完之後未及時釋放。

        for (int i = 0; i < 10; i++) {
Object obj = new Object();
list.add(obj);
obj = null;
}

此時list是一個靜態的集合,obj單個對象,當list集合使用完畢,應當及時清除該集合,避免obj被靜態對象引用。

3、 匿名內部類&非靜態內部類

Android 中常見的是對ListView中各個元素設置點擊事件,如果此時採用匿名內部類,會存在內存洩露的風險。常規做法是將該點擊事件用接口和setTag()的方式往外傳遞。

同樣Handler在使用過程中也會出現內存洩露的風險,一般則是採用弱引用的方式處理,或者在Activity 的onDestroy方法中移除該Handler的所有消息`handler.removeCallbacksAndMessages(null)``。

4、線程洩露

在主線程Activity中出現如下代碼:

    public void onCreate(Bundle icicle) {
super.onCreate(icicle);
mIntent = (icicle == null) ? getIntent() : null;
new Thread(new Runnable() {
@Override
public void run() {
doSomeThing();
}
}).start();
}

當此時該Activity已經銷毀,但是子線程中doSomeThing方法未執行完成,此時會造成內存洩露。一般做法是當Activity銷毀時取消該線程或者採用其他方式實現,總之原則是線程不持有Activity的上下文,如果持有,就應及時取消。

5、資料庫遊標,文件資源未及時關閉,廣播未反向註冊,服務未解綁等行為。

6、Bitmap加載洩露

Bitmap的加載在Android一直是比較吃內存的,且容易出現內存洩露相關問題,一般都是採用統一的圖片請求框架去處理圖片加載緩存,這些框架都會從加載、壓縮、緩存等策略對其做優化處理。同時Google 官方也是推薦使用統一庫處理位圖,具體可以在Glide官網查看。關於圖片加載這塊其實是很容易出現內存洩露的問題,在此暫時不作展開,後續會細說。

5.常用工具5.1 Memory Profiler

Memory Profiler 是 Android Profiler 中的一個組件,可幫助您識別可能會導致應用卡頓、凍結甚至崩潰的內存洩漏和內存抖動。它顯示一個應用內存使用量的實時圖表,可以捕獲堆轉儲、強制執行垃圾回收以及跟蹤內存分配。從圖中現象

可以看出應用此時存在內存抖動的現象,此時抓取紅色部分,可以得到如下圖:

-D選中C區域中任一對象,即可看見具體類為MainActivity,且可以看到行數,右擊Jump to Source即可以跳入具體代碼。5.2 Memory Analyzer

Memory Analyzer MAT是一個功能豐富的 JAVA 堆轉儲文件分析工具,可以幫助開發者發現內存漏洞和減少內存消耗。根據上面分析下面這段代碼是存在內存洩露的問題:

public class CallBackManager {

public static ArrayList<CallBack> sCallBacks = new ArrayList<>();

public static void addCallBack(CallBack callBack) {
sCallBacks.add(callBack);
}

public static void removeCallBack(CallBack callBack) {
sCallBacks.remove(callBack);
}

}

public class MemoryLeakActivity extends AppCompatActivity implements CallBack{

@Override
protected void onCreate( Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_memoryleak);
ImageView imageView = findViewById(R.id.iv_memoryleak);
Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.splash);
imageView.setImageBitmap(bitmap);
CallBackManager.addCallBack(this);
}

@Override
protected void onDestroy() {
super.onDestroy();

}

@Override
public void doWork() {

}
}

連續進入退出MemoryLeakActivity,通過Android Studio 的Profile可以查看到當時的內存入下圖

可以看到此時內存處於一個波動狀態,保存該文件,生成memory-20191212T110510.hprof文件,通過命令轉換

D:\>hprof-conv D:\Android\log\memory-20191212T110510.hprof D:\Android\log\2.hprof

此時通過MAT打開轉換後的文件如下:

選擇Histogram,輸入正則表達".MemoryLeak. "可以搜索到具體包名,然後右鍵List objects->With incoming references然後選擇Path to GC Roots->With all references(此處可以選擇其他)。此時可以看到下面這張圖

從圖中即可看出內存洩露的位置,即該Activity被對象sCallbacks引用。在代碼中添加方法

    protected void onDestroy() {
super.onDestroy();
CallBackManager.removeCallBack(this);
}

再次抓取內存信息,並未出現上述結果。

5.3 LeakCanary

可以通過LeakCanary在開發階段檢測到引用的內存情況。LeakCanary 主要是通過監聽Activity的onDestory,手動調用GC,然後通過ReferenceQueue+WeakReference,來判斷Activity對象是否被回收,然後結合dump Heap的hpof文件,通過Haha開源庫分析洩露的位置。具體使用可以參照leakcanary。

6. 總結

關於內存優化知識點很多,很細。但究其根本我認為是監控內存洩露和優化內存洩漏,各大廠商都有提過相關的方案

這些都具備參考價值。同時我們也可以採用一些Hook黑科技相關方法進行部分內存性能消耗較大的業務進行監控,及時告知開發人員。例如:可以通過Epic監控項目中所有的setImageBitmap()方法,此時就可以知道傳入的Bitmap是否有內存相關風險,一旦有風險,立馬通知反饋。

以上為此次Android內存優化的總結,歡迎指正。

感謝:

https://developer.android.google.cn/topic/performance/memory-overviewhttps://time.geekbang.org/column/article/71610https://coding.imooc.com/learn/list/308.html

相關焦點

  • Android性能優化:手把手帶你全面實現內存優化
    最近有想換工作的同學們,可參考《5月技術崗位內推|RN開發招聘啦》,再往下看,一篇關於性能優化的好文章,很值得去學習。前言在 Android開發中,性能優化策略十分重要本文主要講解性能優化中的內存優化,希望你們會喜歡目錄
  • Android性能優化:帶你全面實現內存優化
    今日限免課程:《Android性能優化詳解—內存篇》(滑動查看課程目錄)課程免費
  • 最全的Android內存優化技巧
    本文主要介紹性能優化的一些手段,但是為了便於理解以及融會貫通,建議先了解Android內存管理機制,本文將從四個角度來介紹內存優化技巧減小對象的內存佔用儘量減少新分配出來的對象佔用內存的大小,使用更加輕量的對象1.
  • android內存優化總結
    所以PSS值除了自身應用佔有的內存外還包括共享內存中比例分配到單個應用身上的內存,所以我覺得用這個值來定義是否進行了優化是比較合適的。使用核心然後再退出的功能,查看PSS值是否飆升或者在使用後長時間不降低下來如果遇到飆升雖然後續能降低下來,但是依然有可能OOM,這樣我們也需要去追查是什麼原因了,看如何能夠減少內存的使用。
  • Android性能優化典範
    摘要:Google在Udacity上的《Android性能優化》在線課程詳細介紹了該如何優化性能,這些課程是Google之前在Youtube上發布的Android性能優化典範專題課程的細化與補充。本文是對渲染、運算、內存、電量四個篇章的學習筆記。
  • 高頻面試點:Android性能優化之內存優化(上篇)
    眾所周知,內存優化可以說是性能優化中最重要的優化點之一,可以說,如果你沒有掌握系統的內存優化方案,就不能說你對Android的性能優化有過多的研究與探索。本篇,筆者將帶領大家一起來系統地學習Android中的內存優化。
  • 幾乎是史上最全最實用的Android性能全面分析與優化方案研究
    藉助性能優化工具分析解決問題性能優化指標性能問題分類1、渲染問題: 過度繪製、布局冗雜2、內存問題: 內存浪費(內存管理)、內存洩漏3、功耗問題: 耗電性能優化原則和方法1、性能優化原則堅持性能測試(開發和測試同學的測試方法略有不同):不要憑感覺去檢測性能問題、評估性能優化的效果,應該保持足夠多的測量,用數據說話(主要針對測試同學)。
  • Android 性能優化之內存洩漏,使用MAT&LeakCanary解決問題
    本文較長,閱讀大約5分鐘App進行到最終的測試的時候,往往會出現一些性能上,以及內存上的問題,需要優化,這也是一個Android高級工程師所需要了解並且掌握的知識點
  • Android性能優化總結
    這是來自一位粉絲「MeloDev」的投稿,講真,我這裡投稿的不少,但是只有我自己覺得很不錯的才會通過,這篇文章我覺得對大家有用,而且性能優化也算是我面試必問的一個話題了,所以這裡推薦給大家。性能指標1. 布局複雜度:布局複雜會導致布局需要更長的時間,從而導致進入應用慢、頁面切換慢;2. 耗電量:耗電量大會導致機器發熱、縮短機器的有效使用時長;3. 內存:內存消耗大會導致頻繁GC,GC時會暫停其它工作,導致頁面卡頓;內存洩露會導致剩餘可用內存越來越小;內存不足會導致應用異常;4.
  • android 復用 布局優化專題及常見問題 - CSDN
    在布局優化中,Androi的官方提到了這三種布局<include />、<merge />、<ViewStub />,並介紹了這三種布局各有的優勢,下面也是簡單說一下怎麼使用.
  • Google《Android性能優化》學習筆記
    Google近期在Udacity上發布了Android性能優化的在線課程,分別從渲染,運算與內存,電量幾個方面介紹了如何去優化性能,這些課程是Google之前在Youtube上發布的Android性能優化典範專題課程的細化與補充。
  • Android性能優化典範(一)
    11) Performance Cost of Memory Leaks雖然Java有自動回收的機制,可是這不意味著Java中不存在內存洩漏的問題,而內存洩漏會很容易導致嚴重的性能問題。內存洩漏指的是那些程序不再使用的對象無法被GC識別,這樣就導致這個對象一直留在內存當中,佔用了寶貴的內存空間。
  • Google 發布 Android 性能優化典範 - OSCHINA - 中文開源技術交流...
    課程專題不僅僅介紹了Android系統中有關性能問題的底層工作原理,同時也介紹了如何通過工具來找出性能問題以及提升性能的建議。主要從三個方面展開,Android的渲染機制,內存與GC,電量優化。下面是對這些問題和建議的總結梳理。0)Render Performance大多數用戶感知到的卡頓等性能問題的最主要根源都是因為渲染性能。
  • 性能優化測試中的相關名詞
    當一個APP或遊戲各種功能越來越多時,性能優化的重要性就不言而喻了,況且現在APP或遊戲的功能逐漸趨同,提升用戶體驗已從產品設計本身轉到了APP或遊戲的流暢性上,這也讓越來越多的開發者更加關注性能優化與測試。
  • 為了性能,Glide 做了哪些優化?
    https://juejin.cn/post/6844903953604280328#heading-8Glide的內存優化主要也是對Bitmap的優化,在回答這個問題前,我們可以想想有哪些常見的Bitmap優化手段。1.當圖片大小與View大小不一致時,可以用inSampleSize進行尺寸優化。
  • QQ音樂Android客戶端Web頁面通用性能優化實踐
    QQ音樂 Android 客戶端的 Web 頁面日均 PV 達到千萬量級,然而頁面的打開耗時與 Native 頁面相距甚遠,需要系統性優化。本文將介紹 QQ 音樂 Android 客戶端在進行 Web 頁面通用性能優化過程中的問題、思路、方案和效果,並嘗試對跨端場景的常見瓶頸和對策進行歸納。文章作者:關嶽,QQ音樂客戶端開發工程師。
  • 性能優化-放開那片內存,讓我來!
    性能優化是一個常有的事情,通常來說本人對此沒有過多涉獵,僅分享工作中接觸到的一些內存。內存性能問題有很多方面會造成性能問題,例如:業務流程設計不合理,導致很多沒有必要的計算數據結構選擇不合適緩存使用不當示例假設你已經者使用profiler分析,已經發現內存分配是性能瓶頸:// 來源:公眾號【編程珠璣】// 作者:守望先生// malloc.cc#include <thread
  • 性能優化之PHP優化
    在我們平常寫代碼的過程中,除了資料庫的優化,針對與文件的優化,我們還需要對PHP執行優化,當然對於老司機來說,這都是毛毛雨咯~但是畢竟有新手嘛,於是,我整理這麼一片文章。(未完待續...)性能優化之PHP優化(一):PHP結構1.字符串
  • Android性能優化典範(三)
    4) The price of ENUMs 在StackOverFlow等問答社區常常出現關於在Android系統裡面使用枚舉類型的性能討論,關於這一點,Android官方的Training課程裡面有下面這樣一句話:
  • 系統架構性能優化思路
    來源:https://4m.cn/rN8IB今天談下業務系統性能問題分析診斷和性能優化方面的內容。這篇文章重點還是談已經上線的業務系統後續出現性能問題後的問題診斷和優化重點。資料庫性能調優拿Oracle資料庫來說,影響資料庫性能的因素包括:系統、資料庫、網絡。資料庫的優化包括:優化資料庫磁碟I/O、優化回滾段、優化Rrdo日誌、優化系統全局區、優化資料庫對象。