查詢並修復Android中的記憶體洩露—OutOfMemoryError
【編者按】本文作者為來自南非約翰內斯堡的女程式設計師 Rebecca Franks,Rebecca 熱衷於安卓開發,擁有4年安卓應用開發經驗。有點完美主義者,喜愛美食。
本文系國內ITOM管理平臺 OneAPM 編譯呈現,以下為正文。
Android 程式中很容易出現記憶體洩露問題。毫無戒心的開發者可能每天都會造成一些記憶體洩露,卻不自知。你可能從未注意過這類錯誤,或者甚至都不知道它們的存在。直到你遇到下面這樣的異常:
java.lang.OutOfMemoryError: Failed to allocate a 4308492 byte allocation with 467872 free bytes and 456KB until OOM
at dalvik.system.VMRuntime.newNonMovableArray(Native Method)
at android.graphics.BitmapFactory.nativeDecodeAsset(Native Method)
at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:609)
at android.graphics.BitmapFactory.decodeResourceStream(BitmapFactory.java:444)
at android.graphics.drawable.Drawable.createFromResourceStream(Drawable.java:988)
at android.content.res.Resources.loadDrawableForCookie(Resources.java:2580)
at android.content.res.Resources.loadDrawable(Resources.java:2487)
at android.content.res.Resources.getDrawable(Resources.java:814)
at android.content.res.Resources.getDrawable(Resources.java:767)
at com.nostra13.universalimageloader.core.DisplayImageOptions.getImageOnLoading(DisplayImageOptions.java:134)
啥?這是什麼意思?是說我的點陣圖(bitmap)太大了嗎?
不幸的是,這種堆疊跟蹤往往帶點迷惑性。通常,如果遇到 OutOfMemoryError 錯誤,十有八九是因為記憶體洩露。當筆者第一次遇到這種堆疊跟蹤時,也感到迷惑不解,想著是不是點陣圖太大了……實際上,我那會兒真是大錯特錯。
什麼是記憶體洩露?
記憶體洩露是指程式釋放廢棄記憶體失敗,導致效能受損或出現中斷。
Android 程式中的記憶體洩露是如何產生的?
Android 程式中的記憶體洩露很容易產生,這也是問題的一部分。然而,最大的問題在於 Android Context(上下文) 物件。
每個 app 都有一個全域性的應用上下文物件( getApplicationContext()
)。每個 Activity (活動)都是 Context
的子類,儲存著與當前活動相關的資訊。通常,記憶體洩露都與已洩露的活動(leaked activtiy)相關。
通常,一般的開發者會把上下文物件(context object)傳給需要的執行緒。建立一些靜態的 TextViews 以儲存指向活動的引用。但是,你懂的,這樣可行嗎?
在此情況下,如果使用記憶體監視器就會發現,app 的記憶體使用率不斷增加,正如下面的 Android 記憶體監控器所示:
存在記憶體洩露問題的 app 在執行時,Android 記憶體監控器的情況
解決記憶體洩露問題後,Android 記憶體監控器的情況
如你所見,在第一張圖中,app 永遠都無法回收一部分已經使用的記憶體。在 OutOfMemoryError 錯誤出現之前,它一度使用了300MB的記憶體。而第二張圖則顯示,app 能順利進行垃圾回收,重得一部分記憶體,從而保持相當穩定的記憶體使用量。
如何避免記憶體洩露?
- 避免在 activity 或 fragment 之外傳遞 Context 物件。
永遠永遠不要建立靜態的 Context 或 View 物件,或者將二者儲存於靜態變數中。這是記憶體洩露的首要標誌。
private static TextView textView; //DO NOT DO THIS private static Context context; //DO NOT DO THIS
總是記得在 onPause() 或 onDestroy() 方法中的取消註冊監聽器(listeners)。這包括 Android 監聽器,以及位置服務、顯示管理器服務,還有自定義的一些監聽器。
不要在 AsyncTasks(非同步任務)或後臺執行緒中儲存指向 activities 的強引用。Activity 可能會關閉,但是 AsyncTask 會繼續執行,一直儲存著對該 activity 的引用。
如果可以,使用 Context-application (getApplicationContext()),而不是某個 activity 的 Context 物件。
盡力避免使用非靜態的內部類。將引用儲存至某個 Activity 或 View 內部會導致記憶體洩露。如果不得不儲存引用,請使用 WeakReference。
如何修復記憶體洩露問題?
修復記憶體洩露問題需要許多實踐,不斷嘗試、試錯,才能取得成功。通常,記憶體洩露並不容易定位。值得慶幸的是,有許多現成的工具可以幫你找出潛在的洩露問題。
1、開啟 Android Studio,開啟 Android Monitor(監控器)選項。 2、執行你的應用,從可選應用中進行選擇你的應用,並執行之。 3、在 app 中進行一些操作,以達到類似的效果。譬如筆者,開啟了一個新視訊,播放了50次。(為此,筆者寫了一個測試程式。) 4、此處的關鍵,是在出現 OutOfMemoryException 異常之前捕獲應用的問題。 5、點選 Android 監控器中的記憶體選項。 <img src="http://blog.oneapm.com/tags-Java.html" alt="查詢並修復Android中的記憶體洩露—OutOfMemoryError](http://i2.wp.com/riggaroo.co.za/wp-content/uploads/2016/01/Screen-Shot-2016-01-26-at-11.12.00-PM.png) 6、你會看到一張動態繪製的圖表。準備好之後,點選“啟動垃圾回收(Initiate GC)“(紅色的垃圾卡車圖示)。 7、點選“傾倒 [Java" /> 堆記憶體(Dump Java Heap)”,之後等待數秒。(卡車圖示下面帶有綠色箭頭的圖示)。這會生成一個 .hprof 檔案,你可以用來分析記憶體使用率。 8、不幸的是,Android Studio Hprof 檔案檢視器不具備 Eclipse 記憶體分析器的所有小工具。因此,你需要安裝 MAT。 9、執行下面的指令,將 Android 的 .hprof 檔案轉換為 MAT 能夠理解的格式。(hprof-conv 工具位於 sdk 的平臺工具資料夾下)
./hprof-conv path/file.hprof exitPath/heap-converted.hprof
10、轉換完成後,在 MAT 中開啟該檔案。選擇“洩露疑點報告(Leak Suspects Report)”,之後點選完成。 開啟 Eclipse 記憶體分析器 —— 選擇洩露疑點報告 11、點選頂部的三個藍色柱形圖示,“為任意物件集合建立一個直方圖”。你會看到佔用記憶體的一列物件。 Eclipse 記憶體分析器 — 直方圖 12、檢視這麼多物件或許會讓人摸不到頭腦。其實,你可以根據類名進行過濾,因此筆者建議你在類名過濾器中輸入類名。 查詢並修復Android中的記憶體洩露—OutOfMemoryError 技術分享 第6張根據類名在 Eclipse 記憶體分析器中過濾物件
13、現在,我們看到 VideoDetailActivity
存在9個例項。這顯然是不對的,因為我們其實只需要一個。進一步檢視誰儲存著 VideoDetailActivity
的引用,右鍵點選該專案,選擇“合併垃圾回收根的最短路徑(Merge Paths to Shortest GC Root)”,然後點選“排除所有虛/弱/軟引用(exclude all phantom/weak/soft etc. references)。”
Eclipse 記憶體分析器——合併垃圾回收根的最短路徑
現在,儲存著引用的執行緒就會顯示出來。之後,你可以追根溯源,找到儲存該 activity 引用的具體例項。
14、根據下面的資訊,顯然,有一個 DisplayListener 物件在登記之後從未登出過。 Eclipse 記憶體分析器 — 記憶體洩露識別
因此,對這個此前登記的顯示監聽器(display listener)呼叫登出方法,就能解決此記憶體洩露問題。
DisplayManager displayManager = (DisplayManager) mContext.getSystemService(Context.DISPLAY_SERVICE);displayManager.unregisterDisplayListener(listener);
不過,並非所有的記憶體洩露都這麼容易找到,也有一些非常難找。但是,希望本文能使你開始尋找問題根源,並避免潛在的記憶體洩露問題。此外,還有許多有助於尋找記憶體洩露問題的工具,點選此處進行檢視。
參考連結:
OneAPM Mobile Insight 以真實使用者體驗為度量標準進行 Crash 分析,監控網路請求及網路錯誤,提升使用者留存。訪問 OneAPM 官方網站感受更多應用效能優化體驗,想閱讀更多技術文章,請訪問 OneAPM 官方技術部落格。
本文轉自 OneAPM 官方部落格
原文地址:http://riggaroo.co.za/fixing-memory-leaks-in-android-outofmemoryerror/
相關文章
- 定位並修復 Go 中的記憶體洩漏Go記憶體
- leaks工具查詢記憶體洩露記憶體洩露
- Android中Handler引起的記憶體洩露Android記憶體洩露
- Android 中 Handler 引起的記憶體洩露Android記憶體洩露
- IE中的記憶體洩露記憶體洩露
- [翻譯]查詢Windows記憶體洩露的幾種方法Windows記憶體洩露
- 轉載 ]查詢Windows記憶體洩露的幾種方法Windows記憶體洩露
- JavaScript中的記憶體洩露模式JavaScript記憶體洩露模式
- Android中的Activitys, Threads和記憶體洩露Androidthread記憶體洩露
- Android 記憶體洩露詳解Android記憶體洩露
- Android 檢測記憶體洩露Android記憶體洩露
- 記憶體洩露記憶體洩露
- android Handler導致的記憶體洩露Android記憶體洩露
- 如何查詢記憶體洩漏記憶體
- js記憶體洩露JS記憶體洩露
- JavaScript記憶體洩露JavaScript記憶體洩露
- 記憶體洩露嗎記憶體洩露
- SHBrowseForFolder 記憶體洩露記憶體洩露
- malloc_stats---檢查記憶體洩露的神器記憶體洩露
- Android記憶體洩露分析以及工具的使用Android記憶體洩露
- 如何診斷 Java 中的記憶體洩露Java記憶體洩露
- js記憶體洩露的原因JS記憶體洩露
- Java記憶體洩露的原因Java記憶體洩露
- JAVA 記憶體洩露的理解Java記憶體洩露
- Android 如何避免 Context 記憶體洩露AndroidContext記憶體洩露
- Android 記憶體洩露優化處理Android記憶體洩露優化
- 解Bug之路-記一次JVM堆外記憶體洩露Bug的查詢JVM記憶體洩露
- Android記憶體優化——記憶體洩露檢測分析方法Android優化記憶體洩露
- 記憶體溢位和記憶體洩露記憶體溢位記憶體洩露
- java中如何檢視記憶體洩露Java記憶體洩露
- Lowmemorykiller記憶體洩露分析記憶體洩露
- C++記憶體洩露檢查的5個方法C++記憶體洩露
- linux下檢查記憶體洩露的工具--mtraceLinux記憶體洩露
- 谷歌工程師發誓:Chrome記憶體洩露問題必須修復谷歌工程師Chrome記憶體洩露
- 簡易 查詢VS2008控制檯記憶體洩露記憶體洩露
- 淺談Android開發中記憶體洩露與優化Android記憶體洩露優化
- Android中的記憶體洩漏Android記憶體
- Volley中listener導致的記憶體洩露記憶體洩露