整理平時(shí)遇到Android內(nèi)存泄漏歸納分析心得

整理平時(shí)遇到Android內(nèi)存泄漏歸納分析心得

內(nèi)存泄漏:對于Java來說,就是new出來的Object 放在堆上無法被GC回收证九。

Java 中的內(nèi)存分配

  1. 靜態(tài)儲存區(qū):編譯時(shí)就分配好彭谁,在程序整個(gè)運(yùn)行期間都存在吸奴。它主要存放靜態(tài)數(shù)據(jù)和常量;
  2. 棧區(qū):當(dāng)方法執(zhí)行時(shí)缠局,會在棧區(qū)內(nèi)存中創(chuàng)建方法體內(nèi)部的局部變量则奥,方法結(jié)束后自動(dòng)釋放內(nèi)存;
  3. 堆區(qū):通常存放 new 出來的對象甩鳄。由 Java 垃圾回收器回收逞度。

四種引用類型的介紹

  1. 強(qiáng)引用(StrongReference):JVM 寧可拋出 OOM ,也不會讓 GC 回收具有強(qiáng)引用的對象妙啃;
  2. 軟引用(SoftReference):只有在內(nèi)存空間不足時(shí)档泽,才會被回的對象;
  3. 弱引用(WeakReference):在 GC 時(shí)揖赴,一旦發(fā)現(xiàn)了只具有弱引用的對象馆匿,不管當(dāng)前內(nèi)存空間足夠與否,都會回收它的內(nèi)存燥滑;
  4. 虛引用(PhantomReference):任何時(shí)候都可以被GC回收渐北,當(dāng)垃圾回收器準(zhǔn)備回收一個(gè)對象時(shí),如果發(fā)現(xiàn)它還有虛引用铭拧,就會在回收對象的內(nèi)存之前赃蛛,把這個(gè)虛引用加入到與之關(guān)聯(lián)的引用隊(duì)列中恃锉。程序可以通過判斷引用隊(duì)列中是否存在該對象的虛引用,來了解這個(gè)對象是否將要被回收呕臂∑仆校可以用來作為GC回收Object的標(biāo)志。

內(nèi)存泄漏是指歧蒋?

我們常說的內(nèi)存泄漏是指new出來的Object無法被GC回收土砂,即為強(qiáng)引用。

怎么知道內(nèi)存泄漏呢谜洽?

內(nèi)存泄漏發(fā)生時(shí)的主要表現(xiàn)為內(nèi)存抖動(dòng)萝映,可用內(nèi)存慢慢變少。比如:在Android studio中的Android Monitor#Monitors可以觀察得到內(nèi)存抖動(dòng)情況阐虚。

Andriod中分析內(nèi)存泄漏的工具M(jìn)AT

  1. MAT(Memory Analyzer Tools)是一個(gè) Eclipse 插件序臂,它是一個(gè)快速、功能豐富的JAVA heap分析工具敌呈,它可以幫助我們查找內(nèi)存泄漏和減少內(nèi)存消耗贸宏。
  2. MAT 插件的下載地址: www.eclipse.org/mat
  3. MAT 使用方法介紹:http://www.cnblogs.com/larack/p/6071209.html

使用leakcanary檢測泄漏

  1. LeakCanary: 讓內(nèi)存泄露無所遁形http://www.liaohuqiu.net/cn/posts/leak-canary/
  2. LeakCanary:中文使用說明 http://www.liaohuqiu.net/cn/posts/leak-canary-read-me/

LeakCanary的內(nèi)存泄露提示一般會包含三個(gè)部分:

  1. 第一部分(LeakSingle類的sInstance變量)
  2. 引用第二部分(LeakSingle類的mContext變量),
  3. 導(dǎo)致第三部分(MainActivity類的實(shí)例instance)泄露.

當(dāng)然檢測內(nèi)存泄漏的工具和方法有很多,就不一一列舉了磕洪,感興趣的可以網(wǎng)上查閱一下吭练。

常見的內(nèi)存泄漏案例

實(shí)例來講android中內(nèi)存泄漏分析解法和編寫代碼應(yīng)注意的事項(xiàng)。

1析显、單例模式引起的內(nèi)存泄露

由于單例模式的靜態(tài)特性鲫咽,使得它的生命周期和我們的應(yīng)用一樣長,如果讓單例無限制的持有Activity的強(qiáng)引用就會導(dǎo)致內(nèi)存泄漏谷异。

解決方案

  1. 將該屬性的引用方式改為弱引用;
  2. 如果傳入Context分尸,使用ApplicationContext;

泄漏代碼片段

public class UserInfoBean {
    private static UserInfoBean userInfoBean;
    private Context mContext;
    private UserInfoBean(Context context) {
        this.mContext = context;
    }

    public static UserInfoBean getUserInfoBean(Context context) {
        if (userInfoBean == null) {
            synchronized (UserInfoBean.class) {
                if (userInfoBean == null) {
                    userInfoBean = new UserInfoBean(context);
                }
            }
        }
        return userInfoBean;
    }
}

使用ApplicationContext

public class UserInfoBean {
    private static UserInfoBean userInfoBean;
    private Context mContext;
    private UserInfoBean(Context context) {
        this.mContext = context.getApplicationContext();
    }

    public static UserInfoBean getUserInfoBean(Context context) {
        if (userInfoBean == null) {
            synchronized (UserInfoBean.class) {
                if (userInfoBean == null) {
                    userInfoBean = new UserInfoBean(context);
                }
            }
        }
        return userInfoBean;
    }
}

或者代碼中用到的Context可以使用自己定義的MyApplication中的MyApplication.getInstance獲取。

泄漏代碼片段

private static CommonViewHelper mInstance;
private CommonViewHelper() {
}
public static CommonViewHelper getInstance() {
    if (mInstance == null) {
        synchronized (CommonViewHelper.class) {
            if (mInstance == null) {
                mInstance = new CommonViewHelper();
            }
        }
    }

    return mInstance;
}

private View mView = null;
public void setScrolledView(View scrolledView) {
    mView = scrolledView;
}

使用WeakReference

private static CommonViewHelper mInstance;
private CommonViewHelper() {
}
public static CommonViewHelper getInstance() {
    if (mInstance == null) {
        synchronized (CommonViewHelper.class) {
            if (mInstance == null) {
                mInstance = new CommonViewHelper();
            }
        }
    }

    return mInstance;
}

//使用弱引用 防止內(nèi)存泄漏
private WeakReference<View> mViewWeakRef = null;
public void setScrolledView(View scrolledView) {
    mViewWeakRef = new WeakReference<View>(scrolledView);
}

2歹嘹、Handler引起的內(nèi)存泄漏

Handler引起的內(nèi)存泄漏在開發(fā)中最為常見的箩绍。Handler、Message尺上、MessageQueue都是相互關(guān)聯(lián)在一起的材蛛,如果Handler發(fā)送的Message尚未被處理,那么該Message以及發(fā)送它的Handler對象都會被線程MessageQueue一直持有怎抛,保持到消息得到處理卑吭,而導(dǎo)致了Activity無法被垃圾回收器回收,而導(dǎo)致了內(nèi)存泄露马绝。由于Handler屬于TLS(Thread Local Storage)變量豆赏,生命周期和Activity是不一致的,因此這種實(shí)現(xiàn)方式很難保證跟Activity的生命周期一直,所以很容易無法釋放內(nèi)存掷邦。

解決方案

  1. 可以把Handler類放在單獨(dú)的類文件中白胀,或者使用靜態(tài)內(nèi)部類便可以避免泄露;
  2. 如果想在Handler內(nèi)部去調(diào)用所在的Activity,那么可以在handler內(nèi)部使用弱引用的方式去指向所在Activity.使用Static + WeakReference的方式來達(dá)到斷開Handler與Activity之間存在引用關(guān)系的目的。

泄漏代碼片段

private final Handler mHandler = new Handler() {  
    @Override  
    public void handleMessage(Message msg) {  
        // ...  
    }  
};  
@Override  
public void onCreate(Bundle savedInstanceState) {  
    super.onCreate(savedInstanceState);  
    setContentView(R.layout.activity_main);  
    mHandler.sendMessageDelayed(Message.obtain(), 60000*5);  
}   

在上面的代碼中發(fā)送了一個(gè)延時(shí)5分鐘執(zhí)行的Message耙饰,當(dāng)該Activity退出的時(shí)候纹笼,延時(shí)任務(wù)(Message)還在主線成的MessageQueue中等待,此時(shí)的Message持有Handler的強(qiáng)引用苟跪,并且由于Handler是我們的Activity類的非靜態(tài)內(nèi)部類,所以Handler會持有該Activity的強(qiáng)引用蔓涧,此時(shí)該Activity退出時(shí)無法進(jìn)行內(nèi)存回收件已,造成內(nèi)存泄漏。

正確代碼片段

解決辦法:將Handler聲明為靜態(tài)內(nèi)部類元暴,這樣它就不會持有外部類的引用了篷扩,Handler的的生命周期就與Activity無關(guān)了。不過倘若用到Context等外部類的非static對象茉盏,還是應(yīng)該通過使用Application中與應(yīng)用同生命周期的Context比較合適鉴未。

private final MyHandler mHandler = new MyHandler(this);
@Override  
public void onCreate(Bundle savedInstanceState) {  
    super.onCreate(savedInstanceState);  
    setContentView(R.layout.activity_main);  
    mHandler.sendMessageDelayed(Message.obtain(), 60000*5);
}  

private static final class MyHandler extends Handler {
        private WeakReference<HomeMainActivity> mActivity;

        public MyHandler(HomeMainActivity mainActivity) {
            mActivity = new WeakReference<>(mainActivity);
      //or
      //mActivity=mainActivity.getApplicationContext;
        }

        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            HomeMainActivity mainActivity = mActivity.get();
            if (null != mActivity) {
                //相關(guān)處理
            }
        }
    }

雖然我們結(jié)束了Activity的內(nèi)存泄漏問題,但是經(jīng)過Handler發(fā)送的延時(shí)消息還在MessageQueue中鸠姨,Looper也在等待處理消息铜秆,所以我們要在Activity銷毀的時(shí)候處理掉隊(duì)列中的消息。

@Override
protected void onDestroy() {
    super.onDestroy();
    //傳入null讶迁,就表示移除所有Message和Runnable
    if (mHandler != null) {
        mHandler.removeCallbacksAndMessages(null);
    }
    mHandler = null;
}

3连茧、InnerClass匿名內(nèi)部類引起的內(nèi)存泄漏(非靜態(tài)內(nèi)部類、匿名內(nèi)部類巍糯、線程 )

在Java中啸驯,非靜態(tài)內(nèi)部類 和 匿名類 都會潛在的引用它們所屬的外部類,但是祟峦,靜態(tài)內(nèi)部類卻不會罚斗。如果這個(gè)非靜態(tài)內(nèi)部類實(shí)例做了一些耗時(shí)的操作,就會造成外圍對象不會被回收宅楞,從而導(dǎo)致內(nèi)存泄漏针姿。

匿名內(nèi)部類的類型可以是如下幾種方式。

  1. 接口匿名內(nèi)部類
  2. 抽象類匿名內(nèi)部類
  3. 類匿名內(nèi)部類

解決方案

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

泄漏代碼片段

public class MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        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 MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        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();
    }
}

Android開發(fā)經(jīng)常會繼承實(shí)現(xiàn) Activity 或者 Fragment 或者 View溉愁。如果使用了匿名類,而又被異步線程所引用,如果沒有任何措施同樣會導(dǎo)致內(nèi)存泄漏的:

public class MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_inner_bad);

        Runnable runnable1 = new MyRunnable();
        Runnable runnable2 = new Runnable() {
            @Override
            public void run() {

            }
        };
    }

    private static class MyRunnable implements Runnable{

        @Override
        public void run() {

        }
    }
}

runnable1 和 runnable2的區(qū)別就是拐揭,runnable2使用了匿名內(nèi)部類撤蟆,runnable1是沒有什么特別的。是個(gè)靜態(tài)內(nèi)部類堂污。但runnable2多出了一個(gè)MainActivity的引用家肯,若是這個(gè)引用再傳入到一個(gè)異步線程,此線程在和Activity生命周期不一致的時(shí)候盟猖,也就造成了Activity的泄露讨衣。

泄漏代碼片段

public class MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        timer();
    }
    //這兒發(fā)生泄漏
    void timer(){
        new Timer().schedule(new TimerTask() {
          @Override
          public void run() {
            while(true);
          }
        },1000 ); // 1秒后啟動(dòng)一個(gè)任務(wù)
      }
}

正確代碼片段

private TimerTask timerTask ;
public class MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        timer();
    }
    void timer(){
        timerTask = new MyTimerTask() ;
        new Timer().schedule( timerTask ,1000 ); // 1秒后啟動(dòng)一個(gè)任務(wù)
      }
      
      private static class MyTimerTask extends TimerTask{
      
        @Override
        public void run() {
          while(true){
            Log.d( "ttttttttt" , "timerTask" ) ;
          }
        }
      }
      
  @Override
  protected void onDestroy() {
    super.onDestroy();
  
    //取消定時(shí)任務(wù)
    if ( timerTask != null ){
      timerTask.cancel() ;
    }
  }
}

注意:在網(wǎng)上看到一些資料說,解決TimerTask內(nèi)存泄漏可以使用在適當(dāng)?shù)臅r(shí)機(jī)進(jìn)行Cancel式镐。經(jīng)過測試反镇,證明單單使用在適當(dāng)?shù)臅r(shí)機(jī)進(jìn)行Cancel,還是有內(nèi)存泄漏的問題娘汞。所以一定要用靜態(tài)內(nèi)部類配合使用歹茶。

4、集合引發(fā)的內(nèi)存泄漏

我們通常把一些對象的引用加入到了集合容器(比如ArrayList)中你弦,當(dāng)我們不需要該對象時(shí)惊豺,并沒有把它的引用從集合中清理掉,這樣這個(gè)集合就會越來越大禽作。如果這個(gè)集合是static的話尸昧,那情況就更嚴(yán)重了。所以要在退出程序之前领迈,將集合里的東西clear彻磁,然后置為null,再退出程序狸捅。

解決方案

在Activity退出之前衷蜓,將集合里的東西clear,然后置為null尘喝,再退出程序磁浇。

正確代碼片段

private List<String> nameList;
private List<Fragment> list;

@Override
public void onDestroy() {
    super.onDestroy();
    if (nameList != null){
        nameList.clear();
        nameList = null;
    }
    if (list != null){
        list.clear();
        list = null;
    }
}

5、 Activity Context 的不正確使用引起的內(nèi)存泄漏

在Android應(yīng)用程序中通承嗤剩可以使用兩種Context對象:Activity和Application置吓。當(dāng)類或方法需要Context對象的時(shí)候常見的做法是使用第一個(gè)作為Context參數(shù)。這樣就意味著View對象對整個(gè)Activity保持引用缔赠,因此也就保持對Activty的所有的引用衍锚。

假設(shè)一個(gè)場景,當(dāng)應(yīng)用程序有個(gè)比較大的Bitmap類型的圖片嗤堰,每次旋轉(zhuǎn)是都重新加載圖片所用的時(shí)間較多戴质。為了提高屏幕旋轉(zhuǎn)是Activity的創(chuàng)建速度,最簡單的方法時(shí)將這個(gè)Bitmap對象使用Static修飾。 當(dāng)一個(gè)Drawable綁定在View上告匠,實(shí)際上這個(gè)View對象就會成為這份Drawable的一個(gè)Callback成員變量戈抄。而靜態(tài)變量的生命周期要長于Activity。導(dǎo)致了當(dāng)旋轉(zhuǎn)屏幕時(shí)后专,Activity無法被回收划鸽,而造成內(nèi)存泄露。

解決方案

  1. 使用ApplicationContext代替Activity#Context戚哎,因?yàn)锳pplicationContext會隨著應(yīng)用程序的存在而存在裸诽,而不依賴于activity的生命周期;
  2. 對Context的引用不要超過它本身的生命周期建瘫,慎重的對Context使用“static”關(guān)鍵字崭捍。Context里如果有線程,一定要在onDestroy()里及時(shí)停掉啰脚。

泄漏代碼片段

private static Drawable sBackground;
@Override
protected void onCreate(Bundle state) {  
    super.onCreate(state);
    TextView tv = new TextView(this);
    tv.setText("引起的內(nèi)存泄漏");  
    if (sBackground == null) {
        sBackground = getDrawable(R.drawable.large_bitmap);
    }
    tv.setBackgroundDrawable(sBackground);
    setContentView(tv);
}

正確代碼片段

private static Drawable sBackground;
@Override
protected void onCreate(Bundle state) {  
    super.onCreate(state);
    TextView tv = new TextView(this);
    tv.setText("引起的內(nèi)存泄漏");  
    if (sBackground == null) {
        sBackground = getApplicationContext().getDrawable(R.drawable.large_bitmap);
    }
    tv.setBackgroundDrawable(sBackground);
    setContentView(tv);
}

6、構(gòu)造Adapter時(shí)实夹,沒有使用緩存的ConvertView引起的內(nèi)存泄漏

  1. 初始時(shí)ListView會從Adapter中根據(jù)當(dāng)前的屏幕布局實(shí)例化一定數(shù)量的View對象橄浓,同時(shí)ListView會將這些View對象 緩存起來。
  2. 當(dāng)向上滾動(dòng)ListView時(shí)亮航,原先位于最上面的List Item的View對象會被回收荸实,然后被用來構(gòu)造新出現(xiàn)的最下面的List Item。
  3. 這個(gè)構(gòu)造過程就是由getView()方法完成的缴淋,getView()的第二個(gè)形參View ConvertView就是被緩存起來的List Item的View對象(初始化時(shí)緩存中沒有View對象則ConvertView是null)准给。

7、BroadcastReceiver重抖、ContentObserver露氮、File、Cursor钟沛、Stream畔规、Bitmap等資源引起的內(nèi)存泄漏

資源性對象比如(Cursor,F(xiàn)ile文件等)往往都用了一些緩沖恨统,我們在不使用的時(shí)候叁扫,應(yīng)該及時(shí)關(guān)閉它們,以便它們的緩沖及時(shí)回收內(nèi)存畜埋。它們的緩沖不僅存在于 java虛擬機(jī)內(nèi)莫绣,還存在于java虛擬機(jī)外。如果我們僅僅是把它的引用設(shè)置為null,而不關(guān)閉它們悠鞍,往往會造成內(nèi)存泄漏对室。因?yàn)橛行┵Y源性對象,比如SQLiteCursor(在析構(gòu)函數(shù)finalize(),如果我們沒有關(guān)閉它,它自己會調(diào)close()關(guān)閉)软驰,如果我們沒有關(guān)閉它涧窒,系統(tǒng)在回收它時(shí)也會關(guān)閉它,但是這樣的效率太低了锭亏。因此對于資源性對象在不使用的時(shí)候纠吴,應(yīng)該調(diào)用它的close()函數(shù),將其關(guān)閉掉慧瘤,然后才置為null. 在我們的程序退出時(shí)一定要確保我們的資源性對象已經(jīng)關(guān)閉戴已。

調(diào)用onRecycled()

@Override
public void onRecycled() {
    reset();
    mSinglePicArea.onRecycled();
}

在View中調(diào)用reset()

public void reset() {
    if (mHasRecyled) {            
        return;
    }
    ...
    SubAreaShell.recycle(mActionBtnShell);
    mActionBtnShell = null;
    ...
    mIsDoingAvatartRedPocketAnim = false;        
    if (mAvatarArea != null) {
            mAvatarArea.reset();
    }        
    if (mNickNameArea != null) {
        mNickNameArea.reset();
    }
}

在自定義 View中取屬性值,調(diào)用recycle()

TypedArray a  = context.getTheme().obtainStyledAttributes(attrs, R.styleable.ArcMenu,defStyleAttr,0);
a.recycle();

8锅减、注冊監(jiān)聽器的泄漏引起的內(nèi)存泄漏

系統(tǒng)服務(wù)可以通過Context.getSystemService 獲取糖儡,它們負(fù)責(zé)執(zhí)行某些后臺任務(wù),或者為硬件訪問提供接口怔匣。如果Context 對象想要在服務(wù)內(nèi)部的事件發(fā)生時(shí)被通知握联,那就需要把自己注冊到服務(wù)的監(jiān)聽器中。然而每瞒,這會讓服務(wù)持有Activity 的引用金闽,如果在Activity onDestory時(shí)沒有釋放掉引用就會內(nèi)存泄漏。

解決方案

  1. 使用ApplicationContext代替ActivityContext;
  2. 在Activity執(zhí)行onDestory時(shí)剿骨,調(diào)用反注冊;

泄漏代碼片段

mSensorManager = (SensorManager) this.getSystemService(Context.SENSOR_SERVICE);

正確代碼片段

mSensorManager = (SensorManager) getApplicationContext().getSystemService(Context.SENSOR_SERVICE);

下面是容易造成內(nèi)存泄漏的系統(tǒng)服務(wù)

泄漏代碼片段

InputMethodManager imm = (InputMethodManager) context.getApplicationContext().getSystemService(Context.INPUT_METHOD_SERVICE);

正確代碼片段

protected void onDetachedFromWindow() {        
       if (this.mActionShell != null) {
           this.mActionShell.setOnClickListener((OnAreaClickListener)null);
       }        
       if (this.mButtonShell != null) { 
           this.mButtonShell.setOnClickListener((OnAreaClickListener)null);
       }        
       if (this.mCountShell != this.mCountShell) {
           this.mCountShell.setOnClickListener((OnAreaClickListener)null);
       }        
       super.onDetachedFromWindow();
}

9代芜、WebView引起的內(nèi)存泄漏

當(dāng)我們不要使用WebView對象時(shí),應(yīng)該調(diào)用它的destory()函數(shù)來銷毀它浓利,并釋放其占用的內(nèi)存挤庇,否則其占用的內(nèi)存長期也不能被回收,從而造成內(nèi)存泄露贷掖。

解決方案

為webView開啟另外一個(gè)進(jìn)程嫡秕,通過AIDL與主線程進(jìn)行通信,WebView所在的進(jìn)程可以根據(jù)業(yè)務(wù)的需要選擇合適的時(shí)機(jī)進(jìn)行銷毀羽资,從而達(dá)到內(nèi)存的完整釋放淘菩。

使用中遇到問題可以參考該鏈接:Android之Android WebView常見問題及解決方案匯總 http://www.cnblogs.com/lee0oo0/p/4026774.html

總結(jié)

其他常見的引起內(nèi)存泄漏原因:

  1. Bitmap在不使用的時(shí)候沒有使用recycle()釋放內(nèi)存。
  2. 非靜態(tài)內(nèi)部類的靜態(tài)實(shí)例容易造成內(nèi)存泄漏:即一個(gè)類中如果你不能夠控制它其中內(nèi)部類的生命周期(譬如Activity中的一些特殊Handler等)屠升,則盡量使用靜態(tài)類和弱引用來處理(譬如ViewRoot的實(shí)現(xiàn))潮改。
  3. 警惕線程未終止造成的內(nèi)存泄露;譬如在Activity中關(guān)聯(lián)了一個(gè)生命周期超過Activity的Thread腹暖,在退出Activity時(shí)切記結(jié)束線程汇在。一個(gè)典型的例子就是HandlerThread的run方法是一個(gè)死循環(huán),它不會自己結(jié)束脏答,線程的生命周期超過了Activity生命周期糕殉,我們必須手動(dòng)在Activity的銷毀方法中調(diào)用thread.getLooper().quit();才不會泄露亩鬼。
  4. 對象的注冊與反注冊沒有成對出現(xiàn)造成的內(nèi)存泄露;譬如注冊廣播接收器阿蝶、注冊觀察者(典型的譬如數(shù)據(jù)庫的監(jiān)聽)等雳锋。
  5. 創(chuàng)建與關(guān)閉沒有成對出現(xiàn)造成的泄露;譬如Cursor資源必須手動(dòng)關(guān)閉羡洁,WebView必須手動(dòng)銷毀玷过,流等對象必須手動(dòng)關(guān)閉等。
  6. 不要在執(zhí)行頻率很高的方法或者循環(huán)中創(chuàng)建對象(比如onMeasure)筑煮,可以使用HashTable等創(chuàng)建一組對象容器從容器中取那些對象辛蚊,而不用每次new與釋放。
  7. 對Context持有一個(gè)過長的引用真仲。對Context的引用超過它本身的生命周期袋马。Android應(yīng)用程序限制使用的堆內(nèi)存是16M 。
  8. 注意對Context的引用不要超過它本身的生命周期 秸应。
  9. Context里假設(shè)有線程虑凛,一定要在onDestory()里及時(shí)停掉。
  10. 當(dāng)類成員變量聲明為static后软啼,它屬于類而不是屬于對象卧檐。假設(shè)我們將非常大的資源對象(Bitmap。context等待)聲明static焰宣。那么這些資源不會被回收的回收目標(biāo)。它會一直存在捕仔。因此匕积,使用statickeyword成員變量定義時(shí)要小心。
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末榜跌,一起剝皮案震驚了整個(gè)濱河市闪唆,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌钓葫,老刑警劉巖悄蕾,帶你破解...
    沈念sama閱讀 217,406評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異础浮,居然都是意外死亡帆调,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,732評論 3 393
  • 文/潘曉璐 我一進(jìn)店門豆同,熙熙樓的掌柜王于貴愁眉苦臉地迎上來番刊,“玉大人,你說我怎么就攤上這事影锈∏畚瘢” “怎么了蝉绷?”我有些...
    開封第一講書人閱讀 163,711評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長枣抱。 經(jīng)常有香客問我熔吗,道長,這世上最難降的妖魔是什么佳晶? 我笑而不...
    開封第一講書人閱讀 58,380評論 1 293
  • 正文 為了忘掉前任桅狠,我火速辦了婚禮,結(jié)果婚禮上宵晚,老公的妹妹穿的比我還像新娘垂攘。我一直安慰自己,他們只是感情好淤刃,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,432評論 6 392
  • 文/花漫 我一把揭開白布晒他。 她就那樣靜靜地躺著,像睡著了一般逸贾。 火紅的嫁衣襯著肌膚如雪陨仅。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,301評論 1 301
  • 那天铝侵,我揣著相機(jī)與錄音灼伤,去河邊找鬼。 笑死咪鲜,一個(gè)胖子當(dāng)著我的面吹牛狐赡,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播疟丙,決...
    沈念sama閱讀 40,145評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼颖侄,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了享郊?” 一聲冷哼從身側(cè)響起览祖,我...
    開封第一講書人閱讀 39,008評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎炊琉,沒想到半個(gè)月后展蒂,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,443評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡苔咪,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,649評論 3 334
  • 正文 我和宋清朗相戀三年锰悼,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片悼泌。...
    茶點(diǎn)故事閱讀 39,795評論 1 347
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡松捉,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出馆里,到底是詐尸還是另有隱情隘世,我是刑警寧澤可柿,帶...
    沈念sama閱讀 35,501評論 5 345
  • 正文 年R本政府宣布,位于F島的核電站丙者,受9級特大地震影響复斥,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜械媒,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,119評論 3 328
  • 文/蒙蒙 一目锭、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧纷捞,春花似錦痢虹、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,731評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至糜值,卻和暖如春丰捷,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背寂汇。 一陣腳步聲響...
    開封第一講書人閱讀 32,865評論 1 269
  • 我被黑心中介騙來泰國打工病往, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人骄瓣。 一個(gè)月前我還...
    沈念sama閱讀 47,899評論 2 370
  • 正文 我出身青樓停巷,卻偏偏與公主長得像,于是被迫代替她去往敵國和親榕栏。 傳聞我的和親對象是個(gè)殘疾皇子叠穆,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,724評論 2 354

推薦閱讀更多精彩內(nèi)容

  • Android 內(nèi)存泄漏總結(jié) 內(nèi)存管理的目的就是讓我們在開發(fā)中怎么有效的避免我們的應(yīng)用出現(xiàn)內(nèi)存泄漏的問題。內(nèi)存泄漏...
    _痞子閱讀 1,636評論 0 8
  • 內(nèi)存管理的目的就是讓我們在開發(fā)中怎么有效的避免我們的應(yīng)用出現(xiàn)內(nèi)存泄漏的問題臼膏。內(nèi)存泄漏大家都不陌生了,簡單粗俗的講示损,...
    宇宙只有巴掌大閱讀 2,363評論 0 12
  • 內(nèi)存管理的目的就是讓我們在開發(fā)中怎么有效的避免我們的應(yīng)用出現(xiàn)內(nèi)存泄漏的問題渗磅。內(nèi)存泄漏大家都不陌生了,簡單粗俗的講检访,...
    DreamFish閱讀 791評論 0 5
  • Android 內(nèi)存泄漏總結(jié) 內(nèi)存管理的目的就是讓我們在開發(fā)中怎么有效的避免我們的應(yīng)用出現(xiàn)內(nèi)存泄漏的問題始鱼。內(nèi)存泄漏...
    apkcore閱讀 1,221評論 2 7
  • 凌晨3點(diǎn),艾蔻從排練場出來脆贵,林的車已經(jīng)在門口等候了医清,司機(jī)老慕拉開車門等候一旁。艾蔻窩在車的后座上習(xí)慣的摸到一個(gè)湯罐...
    施苒閱讀 322評論 0 0