Android App卡頓慢最佳化之解決記憶體抖動及記憶體流失

來源:互聯網
上載者:User

標籤:log   也有   apt   csdn   事先   技術分享   查看   pos   方法   

前面一篇部落格說到了,記憶體抖動的第二種情況,就是必須在短時間內建立對象,但是要控制數量;這個問題目前可以使用對象池的方法解決。

 

 3)Object Pools

在程式裡面經常會遇到的一個問題是短時間內建立大量的對象,導致記憶體緊張,從而觸發GC導致效能問題。對於這個問題,我們可以使用對象池技術來解決它。通常對象池中的對象可能是bitmaps,views,paints等等。關於對象池的操作原理,不展開述說了,請看下面的圖示:

 

使用對象池技術有很多好處,它可以避免記憶體抖動,提升效能,但是在使用的時候有一些內容是需要特別注意的。通常情況下,初始化的對象池裡面都是空白的,當使用某個對象的時候先去對象池查詢是否存在,如果不存在則建立這個對象然後加入對象池,但是我們也可以在程式剛啟動的時候就事先為對象池填充一些即將要使用到的資料,這樣可以在需要使用到這些對象的時候提供更快的首次載入速度,這種行為就叫做預分配。使用對象池也有不好的一面,程式員需要手動管理這些對象的分配與釋放,所以我們需要謹慎地使用這項技術,避免發生對象的記憶體流失。為了確保所有的對象能夠正確被釋放,我們需要保證加入對象池的對象和其他外部對象沒有互相引用的關係。

其實對象池給筆者感覺與線程池相似,不同的是重心不同,線程池考慮的是運行速度提高(使用預先產生空閑線程的方式),對象池更側重與數量(可能是指派至記憶體時間是很短的,所以不需要預分配,導致對象池的預分配優勢不明顯)。

現在問題還沒解決呢,關於解決記憶體抖動,對象池很好,但是僅僅是一個思想概念,沒具體化。怎麼實現呢,筆者推薦使用java的一個LinkedHashMap 這個類,與普通hashmap有不同,就是可以控制數量關於LinkedHashMap 更詳細的資訊,筆者已轉載一篇感覺很棒的關於LinkedHashMap的部落格,點擊這裡可查看。

在這裡Android已提供了一個類可以解決控制數量問題

LRU Cache 通過使用LinkedHashMap實現了LRU Cache (最近最少使用)演算法,這是作業系統的一個演算法,具體的自己百度很多,在這裡不祥細說明。

LRUCache 的實現和使用,筆者也轉載了一篇部落格,感覺很全點擊這裡可查看,看了媽媽再也不用擔心我的Android程式出現記憶體抖動了。

前面一篇結尾也提到了記憶體流失,但是一筆帶過,這裡接著前面記憶體流失的發現和定位,一般情況下,常見發生記憶體流失定位到的地方及解決方案如下:

 

1.集合類

集合類如果僅僅有添加元素的機制,而沒有相應刪除元素機制,這樣就會造成記憶體被佔用,如果這個類是全域性變數(比如類中有靜態屬性,全域性的map等即有靜態引用或final一直指向它)。那麼沒有相應刪除機制,很可能導致集合所佔記憶體只增不減。  解決辦法:在使用集合類時,增加刪除元素機制,並適當調用減少集合所佔記憶體。

2.單例模式

不正確使用單例模式,也會引起記憶體流失單例對象在初始化後將在JVM的整個生命週期存在(以靜態變數方式),如果單例對象持有外部對象的引用,那麼這個外部對象就會一直佔用著記憶體,可能導致記憶體流失(取決於這外部對象是否一致有用)。   解決辦法:單例對象中避免含有不是一直都有用的外部對象引用。

3.Android組件或特殊集合對象的使用

BraodcastReceiver ,ContentObserver,fileObserver,Cursor,Callback等在Activity onDestory或者某類生命週期結束之後一定要unregistere或者close掉,否則這個Activity類會被system強引用,不會被回收。不要直接對Activity進行直接引用作為成員變數,如果不得不這麼做,調用private WeakPeferense mActivity 來做,相同的,對與Service等其他有自己生命週期的對象來說,直接引用都需要考慮是否會存在記憶體泄露的可能。

4.Handler

要知道,只要Handler 發送的Message尚未被處理,則該Message及發送它的Handler對象將被線程MessageQueue一直持有。由於Handler屬於TLS(Thread Local Storage)變數,生命週期和Activity是不一致的。因此這種實現方式一般很難保證跟view或者Activity的生命週期保持一致,故很容易導致無法正確釋放。如上所述,Handler使用要特別小心,否則很可能記憶體流失。   解決辦法:在view 或者Activity生命週期結束前,確保Handler已沒有未處理的訊息(特別是延時訊息)。

5.Thread 記憶體流失

線程也是造成記憶體泄露的一個重要源頭,線程產生記憶體泄露的主要原因在於線程生命週期不可控,比如線程是Activity的內部類,則線程對象中儲存了Activity的一個引用,當線程的run函數耗時較長沒有結束時,線程對象是不會被銷毀的,因此它所引用的老的Activity就出現了記憶體流失問題。解決辦法:1.簡化線程run函數執行的任務,使他在Activity生命週期結束前,任務運行完。2.為Thread增加撤銷機制,當Activity生命週期結束時,將Thread的耗時任務撤銷(筆者推薦這種)。

6.一些不良代碼造成的記憶體壓力  

有些代碼並不造成記憶體流失,但是他們是對沒使用的記憶體沒進行有效及時的釋放,或是沒有有效利用已有的對象而是頻繁的申請新記憶體。

(1) Bitmap 沒調用recycle()

Bitmap 對象在不使用時,我們應該先調用recycle()釋放記憶體,然後才置空,因為載入bitmap對象的記憶體空間,一部分是java的,一部分是c的(因為Bitmap分配的底層是通過jni調用的,Android的Bitmap底層是使用skia圖形庫實現,skia是用c實現的)。這個recycle()函數就是針對c部分的記憶體釋放。

(2)構造Adapter時,沒有使用緩衝的convertView。   解決辦法:使用靜態holdview的方式構造Adapter。

 

 

這樣到這裡記憶體抖動和記憶體流失的發現,定位以及解決方案以說明完畢。

 

Android App卡頓慢最佳化之解決記憶體抖動及記憶體流失

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.