Android效能最佳化(一)--關於記憶體溢出

來源:互聯網
上載者:User

標籤:網路   意思   stream   tle   擷取   cursor   軟體   alt   add   

一個善於自嘲的人,不是因為內心強大,而只是想巧妙地讓別人閉嘴。(心情標籤)

關於記憶體溢出的問題

關於棧記憶體溢出的問題,想必大家或多或少都會遇到過,不像好久不見,卻如膠似漆,不經意間都會碰到<( ̄︶ ̄)>.我同桌曾經還對我那麼一句煽情卻又無比誇張的話,”我敲代碼一天不遇到記憶體溢出,我就難受“,他也是夠了…..
今天就以我目前所瞭解到各種關於記憶體溢出產生的原因和解決方案,分別與大家分享分享,內容不當或知識點理解片面錯誤,還望指出.

1.關於最佳化的瞭解

Android堆記憶體也可自己定義大小

對於一些Android項目,影響效能瓶頸的主要是Android自己記憶體管理機制問題,目前手機廠商對RAM都比較吝嗇,對於軟體的流暢性來說RAM對效能的影響十分敏感,除了 最佳化Dalvik虛擬機器的堆記憶體配置外,
我們還可以強制定義自己軟體的對記憶體大小,我們使用Dalvik提供的dalvik.system.VMRuntime類來設定最小堆記憶體為例:

private final static int CWJ_HEAP_SIZE = 6* 1024* 1024 ;VMRuntime.getRuntime().setMinimumHeapSize(CWJ_HEAP_SIZE); //設定最小heap記憶體為6MB大小。當然對於記憶體吃緊來說還可以通過手動幹涉GC去處理
2.從一個載入圖片時產生記憶體溢出產生的問題開始分析

圖片過大導致OOM
Android中用bitmap時很容易記憶體溢出,比如報如下錯誤:Java.lang.OutOfMemoryError :bitmap size exceeds VM budget。

Picasso.with(context).load(rowItem.getProductImages().get(0)).into(holder.productImageView);錯誤: 2771-2793/com.koove E/art﹕ Throwing OutOfMemoryError "Failed to allocate a 31961100 byte allocation with 4194304 free bytes and 27MB until OOM"03-25 09:53:23.666    2771-2793/com.koove D/skia﹕ --- decoder->decode returned false

1.回到原點:想通過這些代碼實現什麼功能?
載入一些圖片是照片能正常顯示到模擬器中

2.產生的原因?
因為 ImeView 在底層建立圖片層的時候 會佔用很大的記憶體空間,Android載入大量圖片的時候就會造成記憶體溢出

3.分析:
前面說到,imageView 在底層建立圖片層的時候 會佔用很大的記憶體空間,所以我們可以

盡量不要使用setImageBitmap或setImageResource或BitmapFactory.decodeResource來設定一張大圖,因為這些函數在完成decode後,最終都是通過java層的createBitmap來完成的,需要消耗更多記憶體。 因此,改用先通過BitmapFactory.decodeStream方法,建立出一個bitmap,再將其設為ImageView的source.

decodeStream最大的秘密在於其直接調用JNI>>nativeDecodeAsset()來完成decode,無需再使用java層的createBitmap,從而節省了java層的空間

4. 解決或避免的方法:
?解決方案一:
在從網路或本地載入圖片的時候,只載入縮圖。
這個方法的確能夠少佔用不少記憶體,優點是映像在儘可能低的解析度的情況下, 較低的解析度意味著更少的資料儲存在緩衝中。可是它的致命的缺點就是,因為載入的是縮圖,所以圖片失真就比較嚴重咯,

?解決方案二:
讀取圖片時注意方法的調用,適當壓縮

以上代碼可以最佳化記憶體溢出,但它只是改變圖片大小,並不能徹底解決記憶體溢出。

?解決方案三:
運用JAVA的軟引用,及時地recyle()操作 ,進行圖片緩衝,將經常需要載入的圖片,存放在緩衝裡,避免反覆載入。

?解決方案四:
及時銷毀不再使用的Bitmap對象。

3.歸納記憶體溢出緣由

一)是否App中的類中和引用變數過多使用了Static修飾 如public staitc Student s;在類中的屬性中使用 static修飾的最好只用基本類型或字串。如public static int i = 0; //public static String str;

二)是否App中使用了大量的遞迴或無限遞迴(遞迴中用到了大量的建新的對象)

三)是否App中使用了大量迴圈或死迴圈(迴圈中用到了大量的建立的對象)

四)檢查App中是否使用了向資料庫查詢所有記錄的方法。即一次性全部查詢的方法,如果資料量超過10萬多條了,就可能會造成記憶體溢出。所以在查詢時應採用“分頁查詢”。

五)檢查是否有數組,List,Map中存放的是對象的引用而不是對象,因為這些引用會讓對應的對象不能被釋放。會大量儲存在記憶體中。

六)檢查是否使用了“非字面量字串進行+”的操作。因為String類的內容是不可變的,每次運行”+”就會產生新的對象,如果過多會造成新String對象過多,從而導致JVM沒有及時回收而出現記憶體溢出。

4. 什麼情況下回導致記憶體溢出

記憶體溢出的幾點原因:
1. 資源釋放問題
程式碼的問題,長期保持某些資源,如Context、Cursor、IO流的引用,資源得不到釋放
造成記憶體泄露。
2. 對象記憶體過大問題
儲存了多個耗用記憶體過大的對象(如Bitmap、XML檔案),造成記憶體超出限制。
3.static 關鍵字的使用問題
static是Java中的一個關鍵字,當用它來修飾成員變數時,那麼該變數就屬於該類,而不是
該類的執行個體。所以用static修飾的變數,它的生命週期是很長的,如果用它來引用一些資源耗費
過多的執行個體(Context的情況最多),這時就要謹慎對待了。

以上的代碼是很危險的,如果將 Activity 賦值到 mContext 的話。那麼即使該 Activity 已經
onDestroy,但是由於仍有對象儲存它的引用,因此該Activity依然不會被釋放。

針對static的解決方案

1) 應該盡量避免static成員變數引用資源耗費過多的執行個體,比如Context。
2) Context盡量使用ApplicationContext,因為Application的Context的生命週期比較
長,引用它不會出現記憶體泄露的問題。
3) 使用 WeakReference 代替強引用。比如可以使用 WeakReference
mContextRef;
4. 線程導致記憶體溢出
線程產生記憶體泄露的主要原因在於線程生命週期的不可控。

針對這種線程導致的記憶體泄露問題的解決方案:
(一) 將線程的內部類,改為靜態內部類(因為非靜態內部類擁有外部類對象的強引用,而靜
態類則不擁有)。
(二) 線上程內部採用弱引用儲存Context引用。

5.利用工具進行輔助排查

(一)TraceView簡介
Traceview是 Android平台特有的資料擷取和分析工具,它主要用於分析 Android中應用程
序的hotspot(瓶頸)。Traceview本身只是一個資料分析工具,而資料的採集則需要使用Android
SDK中的Debug類或者利用DDMS工具。

(二)heap簡介
heap工具可以協助我們檢查代碼中是否存在會造成記憶體流失的地方。
用heap監測應用進程使用記憶體情況的步驟如下:
1.啟動eclipse後,切換到DDMS透視圖,並確認Devices視圖、Heap視圖都是開啟的;
2.點擊選中想要監測的進程,比如system_process進程;
3.點擊選中Devices視圖介面中最上方一排表徵圖中的“Update Heap”表徵圖;
4.點擊Heap視圖中的“Cause GC”按鈕;
5.此時在Heap視圖中就會看到當前選中的進程的記憶體使用量量的詳細情況。
說明:
a. 點擊“Cause GC”按鈕相當於向虛擬機器請求了一次gc操作;
b. 當記憶體使用量資訊第一次顯示以後,無須再不斷的點擊“CauseGC”,Heap視圖介面會定時
重新整理,在對應用的不斷的操作過程中就可以看到記憶體使用量的變化;
c. 記憶體使用量資訊的各項參數根據名稱即可知道其意思,在此不再贅述。

(三)allocation tracker 簡介

allocation tracker是記憶體配置跟蹤工具
步驟:
運行DDMS,只需簡單的選擇應用進程並單擊Allocation tracker 標籤,就會開啟一個新的視窗,
單擊“Start Tracing”按鈕;
然後,讓應用運行你想分析的代碼。運行完畢後,單擊“Get Allocations”按鈕,一個已指派對象
的列表就會出現第一個表格中。
單擊第一個表格中的任何一項,在表格二中就會出現導致該記憶體配置的棧跟蹤資訊。通過allocation
tracker,不僅知道分配了哪類對象,還可以知道在哪個線程、哪個類、哪個檔案的哪一行。

6. 如何避免OOM異常

1、圖片過大導致OOM
解決方案:
方法1: 等比例縮小圖片

方法2:對圖片採用軟引用,及時地進行recyle()操作

方法3:使用載入圖片框架處理圖片,如專業處理載入圖片的ImageLoader圖片載入架構。還有XUtils的BitMapUtils來做處理

2、介面切換導致OOM
一般情況下,開發中都會禁止橫屏的。因為如果是來回切換話,activity 的生命週期會重新銷毀
然後建立。
有時候我們會發現這樣的問題,橫豎屏切換N次後 OOM了。

解決辦法:
1、看看頁面配置當中有沒有大的圖片,比如背景圖之類的。
去除xml中相關設定,改在程式中設定背景圖(放在onCreate()方法中):

Drawable drawable = getResources().getDrawable(R.drawable.id);    ImageView imageView = new ImageView(this);    imageView.setBackgroundDrawable(drawable);

在Activity destory 時注意,drawable.setCallback(null); 防止Activity得不到及時的釋放

2.或者可以直接把 xml 設定檔載入成 view 再放到一個容器裡,然後直接調用
this.setContentView(View view);方法,避免xml的重複載入。

3、 在頁面切換時儘可能少地重複使用一些代碼
比如:重複調用資料庫,反覆使用某些對象等等……

3.查詢資料庫中有沒有關閉遊標
程式中經常會進行查詢資料庫的操作,如利用一個號碼歸屬地查詢的小demo ,
但是經常會有使用完畢Cursor後沒有關閉的情況。如果
我們的查詢結果集比較小,對記憶體的消耗不容易被發現,只有在常時間大量操作的情況下才會出現內
存問題,這樣就會給以後的測試和問題排查帶來困難和風險。

4、構造Adapter時,沒有使用緩衝的 convertView
在使用ListView的時候通常會使用Adapter,那麼我們應該儘可能的使用ConvertView。
為什麼要使用convertView?
當convertView為空白時,用setTag()方法為每個View綁定一個存放控制項的ViewHolder對象。
當 convertView 不為空白,重複利用已經建立的 view 的時候,使用 getTag()方法擷取綁定的
ViewHolder對象,這樣就避免了findViewById對控制項的層層查詢,而是快速定位到控制項。
5、Bitmap對象不再使用時調用recycle()釋放記憶體
有時我們會手工的操作Bitmap對象,如果一個Bitmap對象比較占記憶體,當它不再被使用的時
候,可以調用Bitmap.recycle()方法回收此對象的像素所佔用的記憶體,但這不是必須的,視情況而定。
6、其他
Android 應用程式中最典型的需要注意釋放資源的情況是在 Activity 的生命週期中,在onPause()、onStop()、 onDestroy()方法中需要適當的釋放資源的情況。使用廣播沒有登出也會產生OOM。

嗯.關於記憶體溢出的問題肯定不止於這些,今天就寫到這,之後遇到再進行更新改進吧.針對肯能出現的錯誤還請大家指出,晚安.<( ̄ˇ ̄)/

Android效能最佳化(一)--關於記憶體溢出

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在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.