Android,誰動了個人內存(1)

1、 Android的內存機制java

    Android的程序由Java語言編寫,因此Android的內存管理與Java的內存管理類似。程序員經過new爲對象分配內存,全部對象在java堆內分配空間;然而對象的釋放是由垃圾回收器來完成的。C/C++中的內存機制是「誰污染,誰治理」,java的就比較人性化了,給咱們請了一個專門的清潔工(GC)。程序員

    那麼GC怎麼可以確認某一個對象是否是已經被廢棄了呢?Java採用了有向圖的原理。Java將引用關係考慮爲圖的有向邊,有向邊從引用者指向引用對象。線程對象能夠做爲有向圖的起始頂點,該圖就是從起始頂點開始的一棵樹,根頂點能夠到達的對象都是有效對象,GC不會回收這些對象。若是某個對象 (連通子圖)與這個根頂點不可達(注意,該圖爲有向圖),那麼咱們認爲這個(這些)對象再也不被引用,能夠被GC回收。ide

2、Android的內存溢出函數

    Android的內存溢出是如何發生的?this

    Android的虛擬機是基於寄存器的Dalvik,它的最大堆大小通常是16M,有的機器爲24M。所以咱們所能利用的內存空間是有限的。若是咱們的內存佔用超過了必定的水平就會出現OutOfMemory的錯誤。spa

爲何會出現內存不夠用的狀況呢?我想緣由主要有兩個:線程

  • 因爲咱們程序的失誤,長期保持某些資源(如Context)的引用,形成內存泄露,資源形成得不到釋放。code

  • 保存了多個耗用內存過大的對象(如Bitmap),形成內存超出限制。對象

3、萬惡的staticblog

    static是Java中的一個關鍵字,當用它來修飾成員變量時,那麼該變量就屬於該類,而不是該類的實例。因此用static修飾的變量,它的生命週期是很長的,若是用它來引用一些資源耗費過多的實例(Context的狀況最多),這時就要謹慎對待了。

public class ClassName {       private static Context mContext;       //省略  }

以上的代碼是很危險的,若是將Activity賦值到麼mContext的話。那麼即便該Activity已經onDestroy,可是因爲仍有對象保存它的引用,所以該Activity依然不會被釋放。

    咱們舉Android文檔中的一個例子。

private static Drawable sBackground;        @Override   protected void onCreate(Bundle state) {     super.onCreate(state);          TextView label = new TextView(this);     label.setText("Leaks are bad");          if (sBackground == null) {       sBackground = getDrawable(R.drawable.large_bitmap);     }     label.setBackgroundDrawable(sBackground);          setContentView(label);   }

    sBackground, 是一個靜態的變量,可是咱們發現,咱們並無顯式的保存Contex的引用,可是,當Drawable與View鏈接以後,Drawable就將View設置爲一個回調,因爲View中是包含Context的引用的,因此,實際上咱們依然保存了Context的引用。這個引用鏈以下:

    Drawable->TextView->Context

    因此,最終該Context也沒有獲得釋放,發生了內存泄露。

    如何纔能有效的避免這種引用的發生呢?

    第一,應該儘可能避免static成員變量引用資源耗費過多的實例,好比Context。

    第2、Context儘可能使用Application Context,由於Application的Context的生命週期比較長,引用它不會出現內存泄露的問題。

    第3、使用WeakReference代替強引用。好比可使用WeakReference<Context> mContextRef;

    該部分的詳細內容也能夠參考Android文檔中Article部分。

4、都是線程惹的禍

    線程也是形成內存泄露的一個重要的源頭。線程產生內存泄露的主要緣由在於線程生命週期的不可控。咱們來考慮下面一段代碼。

public class MyActivity extends Activity {      @Override      public void onCreate(Bundle savedInstanceState) {          super.onCreate(savedInstanceState);          setContentView(R.layout.main);          new MyThread().start();      }        private class MyThread extends Thread{          @Override          public void run() {              super.run();              //do somthing          }      }  }

    這段代碼很日常也很簡單,是咱們常用的形式。咱們思考一個問題:假設MyThread的run函數是一個很費時的操做,當咱們開啓該線程後,將設備的橫屏變爲了豎屏,通常狀況下當屏幕轉換時會從新建立Activity,按照咱們的想法,老的Activity應該會被銷燬纔對,然而事實上並不是如此。

    因爲咱們的線程是Activity的內部類,因此MyThread中保存了Activity的一個引用,當MyThread的run函數沒有結束時,MyThread是不會被銷燬的,所以它所引用的老的Activity也不會被銷燬,所以就出現了內存泄露的問題。

 

    有些人喜歡用Android提供的AsyncTask,但事實上AsyncTask的問題更加嚴重,Thread只有在run函數不結束時纔出現這種內存泄露問題,然而AsyncTask內部的實現機制是運用了ThreadPoolExcutor,該類產生的Thread對象的生命週期是不肯定的,是應用程序沒法控制的,所以若是AsyncTask做爲Activity的內部類,就更容易出現內存泄露的問題。

    這種線程致使的內存泄露問題應該如何解決呢?

    第1、將線程的內部類,改成靜態內部類。

    第2、在線程內部採用弱引用保存Context引用。

    解決的模型以下:

public abstract class WeakAsyncTask<Params, Progress, Result, WeakTarget> extends          AsyncTask<Params, Progress, Result> {      protected WeakReference<WeakTarget> mTarget;        public WeakAsyncTask(WeakTarget target) {          mTarget = new WeakReference<WeakTarget>(target);      }        /** {@inheritDoc} */      @Override      protected final void onPreExecute() {          final WeakTarget target = mTarget.get();          if (target != null) {              this.onPreExecute(target);          }      }        /** {@inheritDoc} */      @Override      protected final Result doInBackground(Params... params) {          final WeakTarget target = mTarget.get();          if (target != null) {              return this.doInBackground(target, params);          } else {              return null;          }      }        /** {@inheritDoc} */      @Override      protected final void onPostExecute(Result result) {          final WeakTarget target = mTarget.get();          if (target != null) {              this.onPostExecute(target, result);          }      }        protected void onPreExecute(WeakTarget target) {          // No default action      }        protected abstract Result doInBackground(WeakTarget target, Params... params);        protected void onPostExecute(WeakTarget target, Result result) {          // No default action      }  }



    事實上,線程的問題並不只僅在於內存泄露,還會帶來一些災難性的問題。因爲本文討論的是內存問題,因此在此不作討論。

    因爲51CTO提示我字數超過限制,因此下文在另外一文中(2)。

本文出自 「winux」 博客,謝絕轉載!

相關文章
相關標籤/搜索