成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

Android內(nèi)存泄漏定位、分析、解決全方案

yiliang / 3221人閱讀

摘要:如果這個(gè)靜態(tài)變量在生命周期結(jié)束后沒(méi)有清空,就導(dǎo)致內(nèi)存泄漏。因此造成內(nèi)存泄露。注冊(cè)沒(méi)取消造成的內(nèi)存泄露這種的內(nèi)存泄露比純的內(nèi)存泄漏還要嚴(yán)重,因?yàn)槠渌恍┏绦蚩赡芤孟到y(tǒng)的程序的對(duì)象比如注冊(cè)機(jī)制。

原文鏈接 更多教程 為什么會(huì)發(fā)生內(nèi)存泄漏
內(nèi)存空間使用完畢之后未回收, 會(huì)導(dǎo)致內(nèi)存泄漏。有人會(huì)問(wèn):Java不是有垃圾自動(dòng)回收機(jī)制么?不幸的是,在Java中仍存在很多容易導(dǎo)致內(nèi)存泄漏的邏輯(logical leak)。雖然垃圾回收器會(huì)幫我們干掉大部分無(wú)用的內(nèi)存空間,但是對(duì)于還保持著引用,但邏輯上已經(jīng)不會(huì)再用到的對(duì)象,垃圾回收器不會(huì)回收它們。

例如

忘記釋放分配的內(nèi)存的。(Cursor忘記關(guān)閉等)。

應(yīng)用不再需要這個(gè)對(duì)象,未釋放該對(duì)象的所有引用。

強(qiáng)引用持有的對(duì)象,垃圾回收器是無(wú)法在內(nèi)存中回收這個(gè)對(duì)象。

持有對(duì)象生命周期過(guò)長(zhǎng),導(dǎo)致無(wú)法回收。

Java判斷無(wú)效對(duì)象的原理

Android內(nèi)存回收管理策略圖:


圖中的每個(gè)圓節(jié)點(diǎn)代表對(duì)象的內(nèi)存資源,箭頭代表可達(dá)路徑。當(dāng)圓節(jié)點(diǎn)與 GC Roots 存在可達(dá)路徑時(shí),表示當(dāng)前資源正被引用,虛擬機(jī)是無(wú)法對(duì)其進(jìn)行回收的(如圖中的黃色節(jié)點(diǎn))。反過(guò)來(lái),如果圓節(jié)點(diǎn)與 GC Roots 不存在可達(dá)路徑,則意味著這塊對(duì)象的內(nèi)存資源不再被程序引用,系統(tǒng)虛擬機(jī)可以在 GC 過(guò)程中將其回收掉。

從定義上講,Android(Java)平臺(tái)的內(nèi)存泄漏是指沒(méi)有用的對(duì)象資源任與GC-Root保持可達(dá)路徑,導(dǎo)致系統(tǒng)無(wú)法進(jìn)行回收。

內(nèi)存泄漏帶來(lái)的危害

用戶對(duì)單次的內(nèi)存泄漏并沒(méi)有什么感知,但當(dāng)泄漏積累到內(nèi)存都被消耗完,就會(huì)導(dǎo)致卡頓,崩潰。

內(nèi)存泄露是內(nèi)存溢出OOM的重要原因之一,會(huì)導(dǎo)致Crash

Android中常見(jiàn)的可能發(fā)生內(nèi)存泄漏的地方 1.在Android開(kāi)發(fā)中,最容易引發(fā)的內(nèi)存泄漏問(wèn)題的是Context。

比如Activity的Context,就包含大量的內(nèi)存引用,一旦泄漏了Context,也意味泄漏它指向的所有對(duì)象。

造成Activity泄漏的常見(jiàn)原因:

Static Activities

在類中定義了靜態(tài)Activity變量,把當(dāng)前運(yùn)行的Activity實(shí)例賦值于這個(gè)靜態(tài)變量。
如果這個(gè)靜態(tài)變量在Activity生命周期結(jié)束后沒(méi)有清空,就導(dǎo)致內(nèi)存泄漏。
因?yàn)閟tatic變量是貫穿這個(gè)應(yīng)用的生命周期的,所以被泄漏的Activity就會(huì)一直存在于應(yīng)用的進(jìn)程中,不會(huì)被垃圾回收器回收。

static Activity activity; //這種代碼要避免

單例中保存Activity

在單例模式中,如果Activity經(jīng)常被用到,那么在內(nèi)存中保存一個(gè)Activity實(shí)例是很實(shí)用的。但是由于單例的生命周期是應(yīng)用程序的生命周期,這樣會(huì)強(qiáng)制延長(zhǎng)Activity的生命周期,這是相當(dāng)危險(xiǎn)而且不必要的,無(wú)論如何都不能在單例子中保存類似Activity的對(duì)象。
舉例:

public class Singleton {
    private static Singleton instance;
    private Context mContext;
    private Singleton(Context context){
        this.mContext = context;
    }

    public static Singleton getInstance(Context context){
        if (instance == null){
            synchronized (Singleton.class){
                if (instance == null){
                    instance = new Singleton(context);
                }
            }
        }
        return instance;
    }
}

在調(diào)用Singleton的getInstance()方法時(shí)傳入了Activity。那么當(dāng)instance沒(méi)有釋放時(shí),這個(gè)Activity會(huì)一直存在。因此造成內(nèi)存泄露。
解決方法:

可以將new Singleton(context)改為new Singleton(context.getApplicationContext())即可,這樣便和傳入的Activity沒(méi)關(guān)系了。

Static Views

同理,靜態(tài)的View也是不建議的

Inner Classes

內(nèi)部類的優(yōu)勢(shì)可以提高可讀性和封裝性,而且可以訪問(wèn)外部類,不幸的是,導(dǎo)致內(nèi)存泄漏的原因,就是內(nèi)部類持有外部類實(shí)例的強(qiáng)引用。 例如在內(nèi)部類中持有Activity對(duì)象

解決方法:

1.將內(nèi)部類變成靜態(tài)內(nèi)部類;
2.如果有強(qiáng)引用Activity中的屬性,則將該屬性的引用方式改為弱引用;
3.在業(yè)務(wù)允許的情況下,當(dāng)Activity執(zhí)行onDestory時(shí),結(jié)束這些耗時(shí)任務(wù);

例如:
發(fā)生內(nèi)存泄漏的代碼:

public class LeakAct extends Activity {  
     @Override
     protected void onCreate(Bundle savedInstanceState) {    
         super.onCreate(savedInstanceState);
         setContentView(R.layout.aty_leak);
         test();
     } 
     //這兒發(fā)生泄漏    
     public void test() {    
         new Thread(new Runnable() {      
             @Override
             public void run() {        
                 while (true) {          
                     try {
                         Thread.sleep(1000);
                     } catch (InterruptedException e) {
                         e.printStackTrace();
                     }
                 }
             }
         }).start();
     }
 }

解決方法:

public class LeakAct extends Activity {  
     @Override
     protected void onCreate(Bundle savedInstanceState) {    
         super.onCreate(savedInstanceState);
         setContentView(R.layout.aty_leak);
         test();
     }  
     //加上static,變成靜態(tài)匿名內(nèi)部類
     public static void test() {    
         new Thread(new Runnable() {     
             @Override
             public void run() {        
                 while (true) {          
                     try {
                         Thread.sleep(1000);
                     } catch (InterruptedException e) {
                         e.printStackTrace();
                     }
                 }
             }
         }).start();
     }
 }

Anonymous Classes

匿名類也維護(hù)了外部類的引用。當(dāng)你在匿名類中執(zhí)行耗時(shí)任務(wù),如果用戶退出,會(huì)導(dǎo)致匿名類持有的Activity實(shí)例就不會(huì)被垃圾回收器回收,直到異步任務(wù)結(jié)束。

原文鏈接 更多教程

Handler

handler中,Runnable內(nèi)部類會(huì)持有外部類的隱式引用,被傳遞到Handler的消息隊(duì)列MessageQueue中,在Message消息沒(méi)有被處理之前,Activity實(shí)例不會(huì)被銷毀了,于是導(dǎo)致內(nèi)存泄漏。
解決方法:

1.可以把Handler類放在多帶帶的類文件中,或者使用靜態(tài)內(nèi)部類便可以避免泄露;
2.如果想在Handler內(nèi)部去調(diào)用所在的Activity,那么可以在handler內(nèi)部使用弱引用的方式去指向所在Activity.使用Static + WeakReference的方式來(lái)達(dá)到斷開(kāi)Handler與Activity之間存在引用關(guān)系的目的.
3.在界面銷毀是,釋放handler資源
@Override
 protected void doOnDestroy() {        
     super.doOnDestroy();        
     if (mHandler != null) {
         mHandler.removeCallbacksAndMessages(null);
     }
     mHandler = null;
     mRenderCallback = null;
 }

同樣還有其他匿名類實(shí)例,如TimerTask、Threads等,執(zhí)行耗時(shí)任務(wù)持有Activity的引用,都可能導(dǎo)致內(nèi)存泄漏。

線程產(chǎn)生內(nèi)存泄露的主要原因在于線程生命周期的不可控。如果我們的線程是Activity的內(nèi)部類,所以MyThread中保存了Activity的一個(gè)引用,當(dāng)MyThread的run函數(shù)沒(méi)有結(jié)束時(shí),MyThread是不會(huì)被銷毀的,因此它所引用的老的Activity也不會(huì)被銷毀,因此就出現(xiàn)了內(nèi)存泄露的問(wèn)題。

要解決Activity的長(zhǎng)期持有造成的內(nèi)存泄漏,可以通過(guò)以下方法:

傳入Application 的 Context,因?yàn)?Application 的生命周期就是整個(gè)應(yīng)用的生命周期,所以這將沒(méi)有任何問(wèn)題。

如果此時(shí)傳入的是 Activity 的 Context,當(dāng)這個(gè) Context 所對(duì)應(yīng)的 Activity 退出時(shí),主動(dòng)結(jié)束執(zhí)行的任務(wù),并釋放Activity資源。

將線程的內(nèi)部類,改為靜態(tài)內(nèi)部類。

因?yàn)榉庆o態(tài)內(nèi)部類會(huì)自動(dòng)持有一個(gè)所屬類的實(shí)例,如果所屬類的實(shí)例已經(jīng)結(jié)束生命周期,但內(nèi)部類的方法仍在執(zhí)行,就會(huì)hold其主體(引用)。也就使主體不能被釋放,亦即內(nèi)存泄露。靜態(tài)類編譯后和非內(nèi)部類是一樣的,有自己獨(dú)立的類名。不會(huì)悄悄引用所屬類的實(shí)例,所以就不容易泄露。

如果需要引用Acitivity,使用弱引用。

謹(jǐn)慎對(duì)context使用static關(guān)鍵字。

2.Bitmap沒(méi)調(diào)用recycle()

Bitmap對(duì)象在不使用時(shí),我們應(yīng)該先調(diào)用recycle()釋放內(nèi)存,然后才設(shè)置為null.

3.集合中對(duì)象沒(méi)清理造成的內(nèi)存泄露

我們通常把一些對(duì)象的引用加入到了集合中,當(dāng)我們不需要該對(duì)象時(shí),并沒(méi)有把它的引用從集合中清理掉,這樣這個(gè)集合就會(huì)越來(lái)越大。如果這個(gè)集合是static的話,那情況就更嚴(yán)重了。
解決方案:

在Activity退出之前,將集合里的東西clear,然后置為null,再退出程序。
4.注冊(cè)沒(méi)取消造成的內(nèi)存泄露

這種Android的內(nèi)存泄露比純Java的內(nèi)存泄漏還要嚴(yán)重,因?yàn)槠渌恍〢ndroid程序可能引用系統(tǒng)的Android程序的對(duì)象(比如注冊(cè)機(jī)制)。即使Android程序已經(jīng)結(jié)束了,但是別的應(yīng)用程序仍然還有對(duì)Android程序的某個(gè)對(duì)象的引用,泄漏的內(nèi)存依然不能被垃圾回收。
解決方案:

1.使用ApplicationContext代替ActivityContext;
2.在Activity執(zhí)行onDestory時(shí),調(diào)用反注冊(cè);
5.資源對(duì)象沒(méi)關(guān)閉造成的內(nèi)存泄露

資源性對(duì)象比如(Cursor,F(xiàn)ile文件等)往往都用了一些緩沖,我們?cè)诓皇褂玫臅r(shí)候,應(yīng)該及時(shí)關(guān)閉它們,以便它們的緩沖及時(shí)回收內(nèi)存。而不是等待GC來(lái)處理。

6.占用內(nèi)存較多的對(duì)象(圖片過(guò)大)造成內(nèi)存溢出

因?yàn)锽itmap占用的內(nèi)存實(shí)在是太多了,特別是分辨率大的圖片,如果要顯示多張那問(wèn)題就更顯著了。Android分配給Bitmap的大小只有8M.
解決方法:

1.等比例縮小圖片

BitmapFactory.Options options = new BitmapFactory.Options(); 
options.inSampleSize = 2;//圖片寬高都為原來(lái)的二分之一,即圖片為原來(lái)的四分之一 

2.對(duì)圖片采用軟引用,及時(shí)地進(jìn)行recycle()操作

//軟引用
 SoftReference bitmap = new SoftReference(pBitmap); 
//回收操作
 if(bitmap != null) {  
       if(bitmap.get() != null && !bitmap.get().isRecycled()){ 
           bitmap.get().recycle(); 
           bitmap = null;  
       } 
} 
7.WebView內(nèi)存泄露(影響較大)

解決方案:

用新的進(jìn)程起含有WebView的Activity,并且在該Activity 的onDestory() 最后加上 System.exit(0); 殺死當(dāng)前進(jìn)程。
檢測(cè)內(nèi)存泄漏的方法

1.使用 靜態(tài)代碼分析工具-Lint 檢查內(nèi)存泄漏

Lint 是 Android Studio 自帶的工具,使用姿勢(shì)很簡(jiǎn)單 Analyze -> Inspect Code 然后選擇想要掃面的區(qū)域即可

對(duì)可能引起泄漏的編碼,Lint 都會(huì)進(jìn)行溫馨提示:

2.LeakCanary 工具

Square 公司出品的內(nèi)存分析工具,官方地址如下:https://github.com/square/lea...
LeakCanary 需要在項(xiàng)目代碼中集成,不過(guò)代碼也非常簡(jiǎn)單,如下的官方示例:

在你的 build.gradle:

dependencies {
 debugImplementation "com.squareup.leakcanary:leakcanary-android:1.6.3"
 releaseImplementation "com.squareup.leakcanary:leakcanary-android-no-op:1.6.3"
 // Optional, if you use support library fragments:
 debugImplementation "com.squareup.leakcanary:leakcanary-support-fragment:1.6.3"
}

在 Application 類:

public class ExampleApplication extends Application {

  @Override public void onCreate() {
    super.onCreate();
    if (LeakCanary.isInAnalyzerProcess(this)) {
      // This process is dedicated to LeakCanary for heap analysis.
      // You should not init your app in this process.
      return;
    }
    LeakCanary.install(this);
    // Normal app init code...
  }
}

當(dāng)內(nèi)存泄漏發(fā)生時(shí),LeakCanary 會(huì)彈窗提示并生成對(duì)應(yīng)的堆存儲(chǔ)信息記錄

-3.Android Monitor

開(kāi)Android Studio,編譯代碼,在模擬器或者真機(jī)上運(yùn)行App,然后點(diǎn)擊

,在Android Monitor下點(diǎn)擊Monitor對(duì)應(yīng)的Tab,進(jìn)入如下界面

在Memory一欄中,可以觀察不同時(shí)間App內(nèi)存的動(dòng)態(tài)使用情況,點(diǎn)擊

可以手動(dòng)觸發(fā)GC,點(diǎn)擊

可以進(jìn)入HPROF Viewer界面,查看Java的Heap,如下圖


Reference Tree代表指向該實(shí)例的引用,可以從這里面查看內(nèi)存泄漏的原因,Shallow Size指的是該對(duì)象本身占用內(nèi)存的大小,Retained Size代表該對(duì)象被釋放后,垃圾回收器能回收的內(nèi)存總和。

擴(kuò)展知識(shí) 四種引用類型的介紹

強(qiáng)引用(StrongReference):JVM 寧可拋出 OOM ,也不會(huì)讓 GC 回收具有強(qiáng)引用的對(duì)象;

軟引用(SoftReference):只有在內(nèi)存空間不足時(shí),才會(huì)被回的對(duì)象;

弱引用(WeakReference):在 GC 時(shí),一旦發(fā)現(xiàn)了只具有弱引用的對(duì)象,不管當(dāng)前內(nèi)存空間足夠與否,都會(huì)回收它的內(nèi)存;

虛引用(PhantomReference):任何時(shí)候都可以被GC回收,當(dāng)垃圾回收器準(zhǔn)備回收一個(gè)對(duì)象時(shí),如果發(fā)現(xiàn)它還有虛引用,就會(huì)在回收對(duì)象的內(nèi)存之前,把這個(gè)虛引用加入到與之關(guān)聯(lián)的引用隊(duì)列中。程序可以通過(guò)判斷引用隊(duì)列中是否存在該對(duì)象的虛引用,來(lái)了解這個(gè)對(duì)象是否將要被回收??梢杂脕?lái)作為GC回收Object的標(biāo)志。

原文鏈接 更多教程

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/77440.html

相關(guān)文章

  • 內(nèi)存 問(wèn)題- 收藏集 - 掘金

    摘要:然而,中依然有可能發(fā)生內(nèi)存泄漏。所以你的安卓快速定位解決內(nèi)存泄漏掘金昨天是個(gè)好日子,程序員的節(jié)日,在這里給所有的程序員送上一份遲到的祝福。應(yīng)用內(nèi)存泄漏的定位分析與解決策略掘金,大家好,我是。 Android 性能優(yōu)化之巧用軟引用與弱引用優(yōu)化內(nèi)存使用 - Android - 掘金前言: 從事Android開(kāi)發(fā)的同學(xué)都知道移動(dòng)設(shè)備的內(nèi)存使用是非常敏感的話題,今天我們來(lái)看下如何使用軟引用與弱...

    TIGERB 評(píng)論0 收藏0
  • 記一次 android 線上 oom 問(wèn)題

    摘要:?jiǎn)栴}分析隨著回滾版本的放量,主端崩潰逐漸回歸正常,進(jìn)一步坐實(shí)了新版本存在問(wèn)題。內(nèi)容非常多但都是重復(fù)的,看起來(lái)進(jìn)程沒(méi)有啟動(dòng),導(dǎo)致連接端一直在進(jìn)行重連。背景公司的主打產(chǎn)品是一款跨平臺(tái)的 App,我的部門負(fù)責(zé)為它提供底層的 sdk 用于數(shù)據(jù)傳輸,我負(fù)責(zé)的是 Adnroid 端的 sdk 開(kāi)發(fā)。sdk 并不直接加載在 App 主進(jìn)程,而是隔離在一個(gè)多帶帶進(jìn)程中,然后兩個(gè)進(jìn)程通過(guò) tcp 連接進(jìn)行通信...

    番茄西紅柿 評(píng)論0 收藏2637

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<