內存溢出html
實質應用程序不能及時釋放內存或者加載到內存上的數據太大而致使的OOM問題java
內存泄漏android
目標類被其餘類持有、致使沒法銷燬、從而致使目標類沒法被調用也沒法銷燬。緩存
窗體(內存)泄露數據結構
是指Activity或者Fragment在Destory的狀況下、自身引用被其餘對象或者線程持有,沒法銷燬。app
棧內存溢出框架
StackOverflowError:應用程序調用中,致使棧空間無限延長,超過了虛擬機的承載能力異步
app使用內存超過3/4而且不是當前的app,那麼系統會回收內存(android.app.ActivityThread)ide
http://www.2cto.com/kf/201512/453248.html
(空進程,死亡進程,後臺進程)
省電方面
消息隊列遵循先進先出(First in First out)的原則來說某些信息或者任務進行排隊等待。android中有本身的消息隊列,如Handler和Broadcastreceiver。
andriod提供了 Handler 和 Looper 來知足線程間的通訊。Handler 先進先出原則。Looper類用來管理特定線程內對象之間的消息交換(Message Exchange)。 1)Looper: 一個線程能夠產生一個Looper對象,由它來管理此線程裏的Message Queue(消息隊列)。 2)Handler: 你能夠構造Handler對象來與Looper溝通,以便push新消息到Message Queue裏;或者接收Looper從Message Queue取出)所送來的消息。 3) Message Queue(消息隊列):用來存放線程放入的消息。 4)線程:UI thread 一般就是main thread,而Android啓動程序時會替它創建一個Message Queue。
Handler的集中建立方式和用法
Handler mHandler = new Handler() { public void handleMessage(Message msg) { switch (msg.what) { case TestHandler.GUIUPDATEIDENTIFIER: myBounceView.invalidate(); break; } super.handleMessage(msg); } };
在工做線程中建立(異步)Handler
class LooperThread extends Thread { public Handler mHandler; public void run() { Looper.prepare(); mHandler = new Handler() { public void handleMessage(Message msg) { } }; Looper.loop(); }
使用HandlerThread建立(異步)Handler
HandlerThread handlerThread = new HandlerThread("my.handlerthread"); handlerThread.start(); Handler mHandler = new Handler(handlerThread.getLooper()){ public void handleMessage(Message msg) { } };
Handler的用法不少,這裏只貼出線程通訊的用法,讀者能夠自行深刻研究其餘用法
class myThread implements Runnable { public void run() { while (!Thread.currentThread().isInterrupted()) { //這裏可使用 new,由於主線程中的消息隊列只有一條 Message message = new Message(); message.what = 1024; TmHandler.sendMessage(message); try { Thread.sleep(100); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } } } }
1.1>Handler關於內存泄露
當咱們這樣寫在一個Activity中時,Android Lint會提示咱們這樣一個 warning: In Android, Handler classes should be static or leaks might occur.。 意思說:在Android中,Handler 類應該是靜態的不然可能發生泄漏 一樣在Eclipse代碼編輯區域的 Handler定義行也會出現相似的警告,通常都會加上supresslint的註解
先來看看,爲何會出現這種問題:
1.當Android程序第一次建立的時候,在主線程同時會建立一個Looper對象。Looper實現了一個簡單的消息隊列,一個接着一個處理Message對象。 程序框架全部主要的事件(例如:屏幕上的點擊時間,Activity生命週期的方法等等)都包含在Message對象中,而後添加到Looper的消息隊列中, 一個一個處理。主線程的Looper存在整個應用程序的生命週期內。
2.當一個Handler對象在主線程中建立的時候,它會關聯到Looper的 message queue 。Message添加到消息隊列中的時候Message 會持有當前Handler引用,當Looper處理到當前消息的時候,會調用Handler#handleMessage(Message).
3.在java中,no-static的內部類會 隱式的 持有當前類的一個引用。static的類則沒有。
綜上三點可知,這種泄露很是危險,Activity或者被持有者(宿主)不能及時釋放內存,Looper也在不斷循環,是致使內存泄露緣由之一,
緣由之二是 Activity未被回收,當Activity處於非活動狀態時,若是handlerMessage的處理致使UI的改變,將會致使窗體泄露,好比彈框,UI改變等。
1.2定義靜態類
/** * 使用靜態的內部類,不會持有當前對象的引用 */ private static class MyHandler extends Handler { private SoftReference<Activity> mActivityReference = null; public MyHandler(SampleActivity activity) { mActivityReference = new SoftReference<Activity>(activity) } @Override public void handleMessage(Message msg) { SampleActivity activity = (SampleActivity )mActivityReference.get(); if (activity != null) { // ... } } public void release() { removeCallbacksAndMessages(null); mActivityReference.clear(); } } private final MyHandler mHandler = new MyHandler(this);
在Activity的OnDestroy中調用
public void onDestroy() { mHandler.release(); }
1.3 Handler替代方案
有些時候,Handler無需本身定義,咱們可使用View自身提供的Handler進行操做
myView.post()
myView.postDelay()
myView.getHandler()
runOnUiThread()
.............................
LocalBroadcastReceiver
BroadcastReceiver
或者使用開源方案EventBus
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2.1 避免全局static化
對於廣播,或者一些Runnable,CallBacks甚至普通內部類,咱們要避免全局化,致使變量不能被釋放,從而對象也不能釋放
這裏所說的全局化是被靜態對象持有,好比單例,全局靜態List,Map等
2.2全局化每每是容易持有對象,所以咱們必須學會
add——remove
addAll——clearAll
register——unregister
bind——unbind
這裏相關的主要有View,ViewTreeObserver,ContentObserver,static Map,static List, static SparseArray,singleObject如
View.addOnAttachStateChangeListener View.addOnLayoutChangeListener ViewTreeObserver.add registerBroadcast bindService registerContentObserver
........
2.3若是全局化對象非要持有Context對象
建議使其持有ApplicationContext對象,而不是Service,BroadCastReceiver或者Activity
舉個栗子
android.support.v4.content.LocalBroadcastManager的getInstance
private static LocalBroadcastManager mInstance; public static LocalBroadcastManager getInstance(Context context) { if(mInstance == null) mInstance = new LocalBroadcastManager(context.getApplicationContext()); return mInstance; }
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Android中圖片有四種屬性,分別是:
ALPHA_8:每一個像素佔用1byte內存
ARGB_4444:每一個像素佔用2byte內存
ARGB_8888:每一個像素佔用4byte內存 (默認)
RGB_565:每一個像素佔用2byte內存
Android默認的顏色模式爲ARGB_8888,這個顏色模式色彩最細膩,顯示質量最高。但一樣的,佔用的內存也最大。 因此在對圖片效果不是特別高的狀況下使用RGB_565(565沒有透明度屬性)
publicstaticBitmapreadBitMap(Contextcontext, intresId) { BitmapFactory.Optionsopt = newBitmapFactory.Options(); opt.inPreferredConfig = Bitmap.Config.RGB_565; opt.inPurgeable = true; opt.inInputShareable = true; //獲取資源圖片 InputStreamis = context.getResources().openRawResource(resId); returnBitmapFactory.decodeStream(is, null, opt); } // 先判斷是否已經回收 if(bitmap != null && !bitmap.isRecycled()){ // 回收而且置爲null bitmap.recycle(); bitmap = null; }
3.1Bitmap調用的時候有個方法是recycle,用來釋放內存
3.2使用SoftReference<Bitmap>,WeakReference<Bitmap>進行小內存引用管理
3.3創建磁盤緩存LruDisk,LruCache,具體須要Http 304文件校驗,Http 206斷點續傳等方面知識
3.4進行圖片壓縮,具體參考http://my.oschina.net/ososchina/blog/495861
具體用法請參考http://my.oschina.net/ososchina/blog/355721
問題一,主類釋放問題:內部類隱式持有主類的當前對象,對於內部類需不須要和Handler同樣進行靜態化,徹底取決於內部類會不會被全局(static/單例)對象引用,若是被全局(static/單例)對象引用可能形成沒法釋放,不然徹底不要過於擔憂,由於內部類對主類對象是強引用。
問題二.循環引用問題:
(所謂循環引用是2個貨2個以上的相互關聯的類的對象互相引用,形成了一種引用閉環問題,這種引用形成的問題是閉環內的全部對象都沒法及時銷燬)
Java的虛擬機機制避免了此類問題,可是爲了App性能,咱們須要特別注意此類問題,儘量提早釋放內存(release memory)。
class MyAdapter extends BaseAdapter { private ListView listView;//用於接收傳遞過來的Context對象 public MyImgAdapter(ListView listView) { super(); this.listView = listView; this.listView.setAdapter(this); //從這裏開始出現循環引用 } @Override public int getCount() { return imgs.length; } @Override public Object getItem(int position) { return position; } @Override public long getItemId(int position) { return position; } @Override public View getView(int position, View convertView, ViewGroup parent) { return convertView; } public void release() { //如下2句選擇一種便可解除循環引用,可是爲了效率,都用吧 this.listView.setAdapter(null); this.listView = null; } }
參考:Android postDelay+Dialog引發的窗體泄露
findViewById自己不須要緩存,由於每次都是樹形遍歷,而LayoutInflater每次都是建立一個新的View,因此必要時使用ViewHolder或者ViewModel進行緩存
this.field = null; --------------------------------------------------------------------------------------------------- ViewGroup container= getWindow().getDecorView().findViewById(android.R.id.content); container.removeAllViews(); --------------------------------------------------------------------------------------------------- Iterator it = mImageList.iterator(); while(it.hasNext()) { Bitmap bmp = it.next(); bmp.recycle(); it.remove(); } --------------------------------------------------------------------------------------------------- mList.clear();
11.避免AsyncTask
沒法清空隊列中的任務、而且不能超過128個任務