Android 內存淺析【泄漏、溢出】【一】

繼續,這張說說一些android中泄漏和溢出的細節點:

一、泄漏根源之Static
    static是Java中的一個關鍵字,當用它來修飾成員變量時,那麼該變量就屬於該類,而不是該類的實例。所以用static修飾的變量,它的生命週期是很長的,如果用它來引用一些資源耗費過多的實例(Context的情況最多),這時就要謹慎處理。

public class Example {  
     private static Context mContext;  
     //省略  
}
以上的代碼一般看來似乎沒什麼問題。但如果將Activity賦值到麼mContext的話。那麼即使該Activity已經onDestroy,但是由於仍有對象保存它的引用,因此該Activity依然不會被釋放。

還沒完呢,再看看下邊的:

private static Drawable sBackground;  
	
	public void example (){
		ImageView iv = new ImageView(this);
		iv.setBackgroundDrawable(sBackground);
	}

 我發現有很多哥們都這麼寫,同樣看起來沒什麼問題,但我們來剖析一下:sBackground, 是一個靜態的變量,但是我們發現,我們並沒有顯式的保存Contex的引用,但是,因爲Imageview是View的子類,當Drawable與View連接之後,Drawable就將View設置爲一個回調:

public class View implements Drawable.Callback

/**
     * The application environment this view lives in.
     * This field should be made private, so it is hidden from the SDK.
     * {@hide}
     */
    protected Context mContext;

由於View中是包含Context的引用的,所以,實際上我們依然保存了Context的引用。所以最終的引用鏈如下:
    Drawable->ImageView->Context

    所以,最終該Context也沒有得到釋放,積累就發生了內存泄露。
    如何纔能有效的避免這種引用的發生呢?
    第一,應該儘量避免static成員變量引用資源耗費過多的實例,比如Context。
    第二、Context儘量使用Application Context,因爲Application的Context的生命週期比較長(具體多次不作詳細解釋,各位去看文檔)。
    第三、使用WeakReference代替強引用。比如可以使用WeakReference<Context> mContextRef(一位前輩提起的,具體不是很清楚,也在琢磨中


二、泄漏根源之線程
    線程也是造成內存泄露的一個重要的源頭。線程產生內存泄露的主要原因在於線程生命週期的不可控。先看段代碼。

new Thread(new Runnable() {
			public void run() {
				// TODO do something
			}
		}).start();

   很平常的一段代碼是吧,也是咱經常這麼搞的。假設問題:假設run函數是一個很費時的操作(通常大數據的加載),在加載過程中(前提線程並沒有結束),將設備的豎屏變爲了橫屏,一般情況下當屏幕轉換時會重新創建Activity(當然你也可以設置不創建新的Activity,在此以創建新的爲論點),按照我們的想法,老的Activity應該會被銷燬纔對,然而事實上並非如此。


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

有些人喜歡用Android提供的AsyncTask,但事實上AsyncTask的問題更加嚴重,Thread只有在run函數不結束時纔出現這種內存泄露問題,然而AsyncTask內部的實現機制是運用了ThreadPoolExcutor,該類產生的Thread對象的生命週期是不確定的,是應用程序無法控制的,因此如果AsyncTask作爲Activity的內部類,就更容易出現內存泄露的問題。
    這種線程導致的內存泄露問題應該如何解決呢(引用前輩的話)?
    第一、將線程的內部類,改爲靜態內部類。
    第二、在線程內部採用弱引用保存Context引用。

public class ExampleAsyncTask<Params, Progress, Result, WeakTarget> extends
			AsyncTask<Params, Progress, Result> {
		
		@Override
		protected final Result doInBackground(Params... params) {
			//TODO: do something
			return null;
		}
	}

小結:事實上,線程的問題並不僅僅在於內存泄露,還會帶來一些災難性的問題。例如:多線程併發安全(推薦下載張孝祥的多線程併發視頻,十分詳細)等等

發佈了58 篇原創文章 · 獲贊 78 · 訪問量 74萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章