內存泄漏和內存溢出的優化

內存泄漏: 對象在內存heap堆中中分配的空間, 當再也不使用或沒有引用指向的狀況下, 仍不能被GC正常回收的狀況。 多數出如今不合理的編碼狀況下, 好比在Activity中註冊了一個廣播接收器, 可是在頁面關閉的時候進行unRegister, 就會出現內存溢出的現象。 一般狀況下, 大量的內存泄漏會形成OOMjava

OOM: 即OutOfMemoery, 顧名思義就是指內存溢出了。 內存溢出是指APP向系統申請超過最大閥值的內存請求, 系統不會再分配多餘的空間, 就會形成OOM error。 在咱們Android平臺下, 多數狀況是出如今圖片不當處理加載的時候。內存管理之道嘛, 無非就是先理解並找出內存泄漏的緣由, 再基於這些反式去合理的編碼, 去防範進而避免內存開銷過大的情形。 學習如何合理的管理內存, 最好先了解內存分配的機制和原理。 只有深層次的理解了內部的原理, 才能真正避免OOM的發生。android

Android APP的所能申請的最大內存大小是多少, 有人說是16MB, 有人又說是24MB。 這種事情, 仍是親自用本身的手機測試下比較靠譜。 測試方式也比較簡單, Java中有個Runtime類, 主要用做APP與運行環境交互, APP並不會爲咱們建立Runtime的實例, 可是Java爲咱們提供了單例獲取的方式Runtime.getRuntime()。 經過maxMemory()方法獲取系統可爲APP分配的最大內存, totalMemory()獲取APP當前所分配的內存heap空間大小。 我手上有兩部手機, 一部Oppo find7, 運行Color OS, 實測最大內存分配爲192MB; 一部天語v9, 運行小米系統, 實測最大內存分配爲100MB。 這下看出點眉目了吧, 因爲Android是開源系統, 不一樣的手機廠商實際上是擁有修改這部分權限能力的, 因此就形成了不一樣品牌和不一樣系統的手機, 對於APP的內存支持也是不同的, 和IOS的恆久100MB是不一樣的。 通常來講, 手機內存的配置越高, 廠商也會調大手機支持的內存最大閥值, 尤爲是如今旗艦機滿天發佈的狀況下。 可是開發者爲了考慮開發出的APP的內存兼容性, 沒法保證APP運行在何種手機上, 只能從編碼角度來優化內存了。算法


Android內存優化的關鍵點。shell

1、 萬惡的static數據庫

static是個好東西, 聲明賦值調用就是那麼的簡單方便, 可是伴隨而來的還有性能問題。 因爲static聲明變量的生命週期實際上是和APP的生命週期同樣的, 有點相似與Application。 若是大量的使用的話, 就會佔據內存空間不釋放, 聚沙成塔也會形成內存的不斷開銷, 直至掛掉。 static的合理使用通常用來修飾基本數據類型或者輕量級對象, 儘可能避免修復集合或者大對象, 經常使用做修飾全局配置項、 工具類方法、 內部類。緩存

2、 無關引用網絡

不少狀況下, 咱們需求用到傳遞引用, 可是咱們沒法確保引用傳遞出去後可否及時的回收。 好比比較有表明性的Context泄漏, 不少狀況下當Activity結束掉後, 因爲仍被其餘的對象指向致使一直遲遲不能回收, 這就形成了內存泄漏。 這時能夠考慮第三條建議。框架

3、 善用SoftReference/WeakReference/LruCache異步

JavaAndroid中有沒有這樣一種機制呢, 當內存吃緊或者GC掃過的狀況下, 就能及時把一些內存佔用給釋放掉, 從而分配給須要分配的地方。 答案是確定的, java爲咱們提供了兩個解決方案。 若是對內存的開銷比較關注的APP, 能夠考慮使用WeakReference, 當GC回收掃過這塊內存區域時就會回收; 若是不是那麼關注的話, 可使用SoftReference, 它會在內存申請不足的狀況下自動釋放, 一樣也能解決OOM問題。 同時Android3.0之後也推出了LruCache類, 使用LRU算法就釋放內存, 同樣的能解決OOM, 若是兼容3.0一下的版本, 請導入v4包。 關於第二條的無關引用的問題, 咱們傳參能夠考慮使用WeakReference包裝一下。工具

4、 謹慎handler

在處理異步操做的時候, handler + thread是個不錯的選擇。 可是相信在使用handler的時候, 你們都會遇到警告的情形, 這個就是lint爲開發者的提
醒。 handler運行於UI線程, 不斷處理來自MessageQueue的消息, 若是handler還有消息須要處理可是Activity頁面已經結束的狀況下, Activity
的引用其實並不會被回收, 這就形成了內存泄漏。 解決方案, 一是在ActivityonDestroy
方法中調用
handler.removeCallbacksAndMessages(null);取消全部的消息的處理, 包括待處理的消息; 二是聲明handler的內部類爲static

5Bitmap終極殺手

Bitmap的不當處理很可能形成OOM, 絕大多數狀況都是因這個緣由出現的。 Bitamp位圖是Android中當之無愧的胖小子, 因此在操做的時候固然是十分的當心了。 因爲Dalivk並不會主動的去回收, 須要開發者在Bitmap不被使用的時候recycle掉。 使用的過程當中, 及時釋放是很是重要的。 同時若是需求容許, 也能夠去BItmap進行必定的縮放, 經過BitmapFactory.OptionsinSampleSize屬性進行控制。 若是僅僅只想得到Bitmap的屬性, 其實並不須要根據BItmap的像素去分配內存, 只需在解析讀取Bmp的時候使用BitmapFactory.OptionsinJustDecodeBounds屬性。 最後建議你們在加載網絡圖片的時候, 使用軟引用或者弱引用並進行本地緩存, 推薦使用android-universal-imageloader或者xUtils, 牛人出品, 必屬精品。 前幾天在講《 自定義控件( 三) 繼承控件》 的時候, 也整理一個, 你們能夠去Github下載看看。

6Cursor及時關閉

在查詢SQLite數據庫時, 會返回一個Cursor, 當查詢完畢後, 及時關閉, 這樣就能夠把查詢的結果集及時給回收掉。

7、 頁面背景和圖片加載

在佈局和代碼中設置背景和圖片的時候, 若是是純色, 儘可能使用color; 若是是規則圖形, 儘可能使用shape畫圖; 若是稍微複雜點, 可使用9patch圖; 若是不能使用9patch的狀況下, 針對幾種主流分辨率的機型進行切圖。

8ListViewGridViewitem緩存

對於移動設備, 尤爲硬件良莠不齊的android生態, 頁面的繪製實際上是很耗時的, findViewById也是蠻慢的。 因此不重用View, 在有列表的時候就尤其顯著了, 常常會出現滑動很卡的現象。 具體參照歷史文章《 說說ViewHolder的另外一種寫法》

9BroadCastReceiverService

綁定廣播和服務, 必定要記得在不須要的時候給解綁。

10I/O

I/O流操做完畢, 讀寫結束, 記得關閉。

11、 線程

線程再也不須要繼續執行的時候要記得及時關閉, 開啓線程數量不易過多, 通常和本身機器內核數同樣最好, 推薦開啓線程的時候, 使用線程池。

12String/StringBuffer

當有較多的字符創須要拼接的時候, 推薦使用StringBuffer


 內存溢出

在安卓的應用程序中,內存溢出主要提要在幾個方面

1.ListView的顯示

咱們在使用ListView的時候,都會給他設置Adapter,若是在Adapter中的getView方法 中,咱們沒有複用convertView,就會形成在滑動ListView的時候,會爲每個item都 生成一個View對象,而無論這個item以前是否已經生成過View對象。若是來回滑動 的次數太多的話,就會形成View生成的數量太多,最終會形成內存溢出。

若是ListView中的item是包含圖片的,那麼,若是在快速滑動的過程當中,咱們就去爲item加載圖片,此時很是容易形成內存溢出。由於,在快速滑動的過程當中,垃圾回收器還來不及回收內存,而新的item又須要新的內存來顯示圖片。因此,在這種狀況下,通常的作法是監聽ListView的滾動狀態,當ListView的滾動狀態爲空閒的狀況下,裏面 的每個Item纔去加載圖片。

2.加載圖片相關

a、加載多張圖片

對於加載多張圖片,咱們通常會使用三級緩存來實現圖片的加載。內存緩存、本地緩存、網絡緩存。緩存的目的是爲了下一次加載速度更快。因此在內存中保存着必定數量的圖片是有助於下一次圖片顯示的速度。可是,內存中不能保存太多的圖片對象,因此咱們使用LRUCache來保存內存中的圖片,而且控制在必定的數量以內。

b、加載單張圖片

這種狀況是針對於某一張圖片特別大的狀況。若是一張圖片很是很是大,若是50M,那麼我只要一去加載它,那麼個人程序確定就會掛,根本還沒使用到三級緩存應用程序就受不了了。因此,對於大圖片的顯示須要特殊處理。由於圖片雖然特別大,可是這個圖片所須要顯示的控件有多是很小的。咱們能夠先把圖片的寬和高獲得,再獲得這張圖片所須要顯示的控件的寬高,就能夠獲得圖片和控件的縮放比例了。最後,根據縮放比例,設置圖片的採樣率,來減少單張圖片的內存佔用。


解決方案

不管是內存泄露仍是內存溢出,最終的後果基本上是一致的,那就是形成應用程序強行關閉。在應用程序的功能開發完以後,怎麼樣才能肯定應用程序有沒有內存的問題呢?又怎樣來肯定究竟是哪一塊代碼出的問題呢?接下來咱們就來講說關於內存問題的解決方案。

1、使用monkey工具

由於有些內存問題藏的比較深,要長期使用才能出現異常。因此可使用monKey工具 來對咱們的應用程序進行壓力測試。

模擬200000次用戶操做的參考命令: adb shell monkey -s 23 -p cn.itcast.XXX(所測試的 包) --ignore-crashes --ignore-timeouts -v -v -v 200000 > D:\文件名.log

回車以後,咱們所須要測試的應用程序就啓動了,而且還有不斷觸摸事件被促發,程序 生成的log會保存在D目錄下。

2、捕獲OOM異常

自定義Application並讓它實現UncaughtExceptionHandler 接口,在onCreate方法中讓本身成爲系統的默認異常處理機制。Thread.setDefaultUncaughtExceptionHandler(this);

這樣子設置以後,若是應用程序出現異常,就會調用Application中的uncaughtException 方法。咱們就在這個方法中判斷異常是否爲OOM異常。若是是OOM異常,就將內存快照導到sd卡中去。

3.用第三方開源框架分析

leakcanary 是一個開源項目,一個內存泄露自動檢測工具,是著名的 GitHub 開源組織 Square 貢獻的,它的主要優點就在於自動化過早的發覺內存泄露、配置簡單、抓取貼心,缺點在於還存在一些bug,不過正常使用百分之九十狀況是OK的,其核心原理與MAT工具相似。

相關文章
相關標籤/搜索