如下內容不考慮非引用類型的數據,或者將其等同爲對應的引用類型看待——一切皆對象。java
再也不使用的對象常駐內存,如靜態變量,或被其它還在使用的對象(生命週期更長)所引用的對象,對應內存沒法回收利用。android
爲了不對象沒法正確、及時被釋放,須要理解:
GC如何回收對象,如何釋放對象?緩存
對象的使用是經過指向它的引用被訪問的,引用被保存在引用類型變量中。網絡
這裏變量
指:dom
類變量:靜態成員變量,成員變量也叫字段。
實例變量:非靜態成員變量。
局部變量:在方法中定義,賦值和使用。
不考慮:參數、返回值、常量。異步
在new一個對象後,其強引用被構造方法返回。
對象的內部類對象,也擁有this$0這樣的強引用指向它。ide
Java有四種引用,分別對應不一樣性質的引用可達性(reachable)——可達指經過此引用訪問到對應的對象。
強引用使用引用對應的類型變量保存,須要手動釋放——設置引用變量爲null。
java的有四種引用,其它三種引用由對應的引用包裹類實現——能夠認爲是特殊類型的引用變量,GC在對待這些引用變量時有不一樣的策略:工具
強引用(StrongReference)
正常聲明的變量都是強引用,即使拋出OutOfMemoryError異常,也不會被回收。須要手動設置變量爲null來釋放引用。學習
軟引用(SoftReference)
僅在內存不足時GC纔會回收軟引用對象。測試
弱引用(WeakReference)
一旦掃描到對象僅擁有弱引用,就回收。GC運行在一個優先級很低的線程,不會那麼「及時」發現。
在經過
引用包裹對象
get得到實際對象時,有可能爲null。可使用一個ReferenceQueue來關聯軟引用和弱引用對象,它們在回收時其引用包裹對象被添加至此隊列。
Java有自動的內存回收機制,在合適的時候,運行時會執行GC來清理掉那些再也不被使用的對象。根據內存須要,程序運行時會不按期屢次執行GC。
Java判斷對象是否再也不使用有多種策略,最終都是和對象的引用相關。
若是對象的引用數量爲0,那麼它顯然是垃圾對象。
此外,Java使用「根對象可達性」來斷定對象是否有效。
在虛擬機中,有一類GC相關的對象被稱做「GC root」。
GC root經過引用變量一級級來找到堆中的每個對象。很顯然,不一樣類型的引用變量,GC對待它們有不一樣的發現(使用其中的引用)策略。
那些最終不能從根對象引用獲得的對象被認爲是不可達對象,也就是可回收對象。
可見,只有強引用須要咱們本身來考慮其釋放的問題。在分析內存泄漏問題時,咱們主要關注對象的強引用。
對象的釋放,就是對其強引用的釋放——將保存此引用的變量設置爲null。另外,若對象包含內部類對象,那麼內部對象的引用也要被釋放。
不一樣的變量它們的默認生命週期是不同的。
均可以「手動」設置爲null來釋放。
方法未返回前,執行域的變量都不會釋放。須要注意一些方法中的變量的及時釋放。
void releaseObject() { Person p = new Person(); p = new Person(); // 釋放 p = null; // 釋放 // more code... } void uglyMethod() { Task task; while(!stop) { task = mBlockingQueue.take(); // 阻塞 //一些針對task的操做。 } } 上面,在take()得到下一個對象賦給task以前,task一直引用着上一個從隊列中得到的Task對象——它沒法被釋放。
引用指向某個對象。
A持有B的引用,那麼此引用的方向從A到B。
A不可釋放,A引用B,那麼B也不可釋放。反之,B引用到了不可釋放的A,對B的釋放沒有影響。
Outgoing Reference:
對於一個對象,查看它擁有的引用變量,能夠知道它所引用的其它對象。
Incoming Reference:
其它對象持有的指向當前對象的引用變量。
若A和B互相引用,這兩個對象則造成一個環形引用,但不是根對象可達,環形引用是能夠被正常回收的。
Android程序偏向更輕量級的對象,更少的內存佔用時間(除去必要的內存緩存),重用避免重複建立。
避免使用枚舉
使用final static int。
多使用final修飾
除非業務須要,首選final修飾,編譯器會優化。
圖片
成熟的庫(Android-Universal-Image-Loader),用多少取多少,及時釋放,緩存。
軟引用和弱引用
能知足須要的話,代替強引用。
池和對象複用
避免對象建立,引發內存抖動。例如知道一個集合是固定大小的話,那麼每次網絡請求結束後更新對象字段值,而不是clear又建立一批新對象。
線程池——好處很少說。使用時注意由於run持久不結束,線程對象對應的字段和局部變量注意泄漏。
Adapter中數據對象
和View的複用。
UI操做的去噪
快速滑動、輸入等。
內部類
優先使用靜態內部類。
匿名內部類老是默認持有外部類對象的引用。
使用ApplicationContext
僅在必要的時候——如dialog——使用Activity,並且注意Activity的Context的及時釋放。
使用具體類而不是接口
例如,HashMap,變量不須要聲明爲Map,這會有更好的執行速度。
不必爲「不存在」的擴展性作犧牲。
在onDestroy中作好清理
主要是引用的釋放,廣播的取消註冊,回調/監聽對象的解除,handler的取消投遞的消息、網絡請求的取消、動畫的中止,線程、其它異步任務和處理等。
「最佳實踐」平時多收集,原則上
:
對於泄漏問題,只有一點,不使用就及時把保持引用的成員變量和局部變量設置爲null。重點注意回調和靜態字段。
典型大對象
能夠從Activity開始,依次排查佔用內存較大的對象的泄漏。一般,一個包含更多其它對象的大對象的釋放,順帶解決了不少對象的泄漏。
網絡,語音,線程,其它異步操做,若是使用到callBack/Listener對象,應該注意這些對象的釋放。
場景:
AudioManager是全局的語音管理對象。
假設播放須要傳遞語音文件路徑並提供回調來控制UI:
在Activity中:
void onCreate() { AudioManager.addListener(new AudioPlayCallBack() { @Override public void startPlay() { } @Override public void stopPlay() { } }); } void onPlayButtonClick() { AudioManager.startPlay(mAudioPath); } void onDestroy() { AudioManager.clearListener(); AudioManager.stopPlay(); }
同回調同樣,通常的,Activity中使用Receiver或Observer對象,在onCreate中開始註冊,在onDestroy中須要解除註冊。
做爲四大組件之一,對象自己建立和釋放不是咱們控制。使用startService和stopService、bingService和unBindService來控制組件對象的生命週期。
一般服務是一直運行在後臺的,避免在服務中保存不使用的對象。
場景:
ServiceConnection conn = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { mCoreService = ((CoreService.CoreServiceBinder) service).getService(); mCoreService.registerConnectionStatusCallback(new IConnectionStatusCallback() { @Override public void connectionStatusChanged(int connectedState, String reason) { // xxx } }); // xxx } /** 在和Service的鏈接「意外」中斷時執行,一般是運行Service的其它進程崩潰後引發。 同一進程中幾乎不會發生(Service死掉了,而此處代碼還在執行...):此方法幾乎不會被執行。 不會移除此鏈接。必須主動調用unbindService來解除鏈接。 */ @Override public void onServiceDisconnected(ComponentName name) { mCoreService.unRegisterConnectionStatusCallback(); // 不執行 mCoreService = null;// 不執行 } };
上面應該在onDestroy中unbindService並移除Activity和Service對象的引用(回調匿名內部類)鏈接。
延遲消息被線程中的MessageQueue持有,在消息未處理前,Message對象引用handler,而handler引用Activity的事情很容易發生。
handler大多數時間也是寫爲匿名內部類——這自己沒什麼。
在onDestroy中:
void onDestroy() { handler.removeCallbacksAndMessages(null); }
Android中Context是「God Object」,它擁有不少運行時須要的全局信息。一般使用第三方庫,系統API時,須要一個
Context時,優先使用Application。若是必須用到Activity的狀況,記得它和匿名內部類是同樣的,不要在三番五次的參數傳遞以後,忘記釋放。
屬性動畫必須手動stop,不然它會一直執行下去,持有Activity的mContext致使Activity對象的泄漏。
少用,注意意外的引用駐留。
簡單的:
ActivityManager管理Activity的集合,在onCreate和onDestroy時從ActivityManager中add和remove掉。
類變量若是是內部類這樣的擁有對外部類的引用:
記得釋放類變量,或者換用靜態內部類,普通類,而後提供對外部類引用的設置和解除。
總而言之:對象是有生命週期的,須要在合適的時間釋放對象的強引用。
學習內存分析工具的使用,在實踐中積累內存泄漏的問題,避免錯誤的代碼。
Android Studio 1.5以上版本有此功能。
能夠快速查看對象個數,佔用內存狀況,「簡單地」分析對象引用狀況。
Java的內存分析工具。
運行程序,GC後dump生成hprof文件,使用MAT分析。
在測試環境,使用LeakCanary實時監測。