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」 博客,謝絕轉載!