查詢並修復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記憶體
- Android 記憶體洩露詳解Android記憶體洩露
- [翻譯]查詢Windows記憶體洩露的幾種方法Windows記憶體洩露
- 轉載 ]查詢Windows記憶體洩露的幾種方法Windows記憶體洩露
- Android記憶體洩漏檢測與修復技巧Android記憶體
- android Handler導致的記憶體洩露Android記憶體洩露
- SHBrowseForFolder 記憶體洩露記憶體洩露
- Android效能最佳化之記憶體洩露Android記憶體洩露
- 解Bug之路-記一次JVM堆外記憶體洩露Bug的查詢JVM記憶體洩露
- 記憶體溢位和記憶體洩露記憶體溢位記憶體洩露
- Android中的記憶體洩漏模式Android記憶體模式
- Lowmemorykiller記憶體洩露分析記憶體洩露
- java中如何檢視記憶體洩露Java記憶體洩露
- Android中使用Handler造成記憶體洩露的分析和解決Android記憶體洩露
- ArkTS 的記憶體快照與記憶體洩露除錯記憶體洩露除錯
- 使用 mtrace 分析 “記憶體洩露”記憶體洩露
- 實戰Go記憶體洩露Go記憶體洩露
- 記一次 Ruby 記憶體洩漏的排查和修復記憶體
- 如何解決JVM OutOfMemoryError記憶體洩漏問題?JVMError記憶體
- Android記憶體洩漏Android記憶體
- Android 記憶體洩漏Android記憶體
- 今我們們來聊聊JVM 堆外記憶體洩露的BUG是如何查詢的JVM記憶體洩露
- Linux記憶體洩露案例分析和記憶體管理分享Linux記憶體洩露
- 如何檢查Javascript中的記憶體洩漏JavaScript記憶體
- nodejs爬蟲記憶體洩露排查NodeJS爬蟲記憶體洩露
- Pprof定位Go程式記憶體洩露Go記憶體洩露
- 簡單的記憶體“洩露”和“溢位”記憶體
- JAVA記憶體洩露的原因及解決Java記憶體洩露
- Python實現記憶體洩露排查的示例Python記憶體洩露
- win10驅動記憶體洩露如何解決_win10記憶體洩露處理方法Win10記憶體洩露
- Android中常見的記憶體洩漏Android記憶體
- netty 堆外記憶體洩露排查盛宴Netty記憶體洩露
- 乾貨分享:淺談記憶體洩露記憶體洩露
- 解決git記憶體洩露問題Git記憶體洩露
- Spring Boot heapdump洩露記憶體分析方法Spring Boot記憶體
- 線上記憶體洩露定位--memleak工具記憶體洩露
- 記一次"記憶體洩露"排查過程記憶體洩露
- Android記憶體洩漏場景Android記憶體
- 一個 Vue 頁面的記憶體洩露分析Vue記憶體洩露