android中的內存泄露查找與常見的內存泄露案例分析

常見的內存泄露查找方法請參見:http://hukai.me/android-performance-patterns/

這篇文章是google發佈的android性能優化典範示例,對於渲染、內存GC與電量消耗都做了好的示範。


這裏我總結了下,android中常見的內存泄露

1、類中調用registerReceiver後未調用unregisterReceiver().

在調用registerReceiver後,若未調用unregisterReceiver,其所佔的內存是相當大的。

這種情況常見於我們在Activity或者Service中動態註冊Receiver,動態註冊的receiver會放到application中的一個map中,在application的生命週期內一直持有,這種情況下,相當於持有對於Activity的引用,這樣就導致內存泄露了。


2、資源對象未關閉導致的內存泄露

典型的是使用sqlite數據庫不釋放Cursor和網絡文件io使用inputstreamoutputstream記得要調用close方法。

在Android中,Cursor是很常用的一個對象,但在寫代碼是,經常會有人忘記調用close, 或者因爲代碼邏輯問題狀況導致close未被調用----將close語句放入finally代碼中。

通常,在Activity中,我們可以調用startManagingCursor或直接使用managedQuery讓Activity自動管理Cursor對象。

但需要注意的是,當Activity介紹後,Cursor將不再可用!

若操作Cursor的代碼和UI不同步(如後臺線程),那沒需要先判斷Activity是否已經結束,或者在調用OnDestroy前,先等待後臺線程結束。


3、Bitmap使用後未調用recycle()

根據SDK的描述,調用recycle並不是必須的。但在實際使用時,Bitmap佔用的內存是很大的,所以當我們不再使用時,儘量調用recycle()以釋放資源。


4、構造Adapter時,沒有使用緩存的convertView

以構造ListView的BaseAdapter爲例,在BaseAdapter中提供了方法:

public View getView(int position, ViewconvertView, ViewGroup parent)

來向ListView提供每一個item所需要的view對象。初始時ListView會從BaseAdapter中根據當前的屏幕布局實例化一定數量的view對象,同時ListView會將這些view對象緩存起來。當向上滾動ListView時,原先位於最上面的list item的view對象會被回收,然後被用來構造新出現的最下面的list item。這個構造過程就是由getView()方法完成的,getView()的第二個形參View convertView就是被緩存起來的list item的view對象(初始化時緩存中沒有view對象則convertView是null)。由此可以看出,如果我們不去使用convertView,而是每次都在getView()中重新實例化一個View對象的話,即浪費資源也浪費時間,也會使得內存佔用越來越大。這種情況不一定爲導致嚴重的內存泄露,會造成內存抖動,畢竟GC回收內存也需要佔用cpu資源。ListView回收list item的view對象的過程可以查看:

android.widget.AbsListView.java --> voidaddScrapView(View scrap) 方法。


5、試着使用關於application的context來替代和activity相關的context

這是一個很隱晦的內存泄漏的情況。有一種簡單的方法來避免context相關的內存泄漏。最顯著地一個是避免context逃出他自己的範圍之外。使用Application context。這個context的生存週期和你的應用的生存週期一樣長,而不是取決於activity的生存週期。如果你想保持一個長期生存的對象,並且這個對象需要一個context,記得使用application對象。你可以通過調用Context.getApplicationContext() or Activity.getApplication()來獲得。

總而言之,想要避免context 相關的內存泄漏 ,記住以下幾點:

· 不要對activity 的context 長期引用( 一個activity 的引用的生存週期應該和activity 的生命週期相同)

· 試着使用關於application的 context 來替代和activity相關的context

· 如果一個acitivity 的非靜態內部類的生命週期不受控制,那麼避免使用它;使用一個靜態的內部類並且對其中的activity 使用一個弱引用。解決這個問題的方法是使用一個靜態的內部類,並且對它的外部類有一WeakReference,就像在ViewRoot中內部類W所做的就是這麼個例子。


6、集合中對象沒清理造成的內存泄漏

我們通常把一些對象的引用加入到了集合中,當我們不需要該對象時,並沒有把它的引用從集合中清理掉,這樣這個集合就會越來越大。如果這個集合是static的話,那情況就更嚴重了。


7、回調callback與listener的及時釋放。

比如某個Activity傳遞一個listener到一個Service,那麼該Service就持有了對這個Activity的引用,如果該Service作爲公共服務組件,比如該Service採用aidl接口調用或者運行在獨立的線程中,就會導致該Activity一直沒辦法被GC回收引發內存泄露。



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章