1. 程式人生 > >Android記憶體洩露檢測工具和實際開發中遇到的記憶體洩露問題解析

Android記憶體洩露檢測工具和實際開發中遇到的記憶體洩露問題解析

介紹

記憶體洩露是平常開發中經常遇到的,有些時候稍不注意就會發生,而且還不易察覺,這就需要工具來幫助檢測。本文主要介紹記憶體檢測工具和我在開發中遇到的記憶體洩露問題和解決方案。

記憶體洩露的原理

具體的原理涉及到虛擬機器垃圾回收機制知識,這裡只為下文作介紹說明基本原理。想深度瞭解的請google。

記憶體洩露是指無用物件(不再使用的物件)持續佔有記憶體或無用物件的記憶體得不到及時釋放,從而造成的記憶體空間的浪費稱為記憶體洩露。

記憶體檢測工具

目前開發流行的檢測主要分兩種。

  1. 功能強大PC端檢測工具,如MemoryAnalyzer執行在PC端抓取Android手機中的dump檔案進行深度分析。
  2. 小而優的Android端檢測工具,如LeakCanary隨App一起安裝會在Android手機桌面安裝的記憶體洩露檢測App

我目前使用的是LeakCanary分析記憶體洩露,好處是LeakCanary會一直檢測debug生成的App,在App執行期間一直分析記憶體洩露情況,並抓取記憶體洩露關鍵程式碼,隨時發現問題。

提供中文說明地址,使用比較簡單就不說明,不瞭解的點進去看看。

容易引起記憶體洩漏的幾大原因

  • 靜態集合類
    像HashMap、Vector等靜態集合類的使用最容易引起記憶體洩漏,因為這些靜態變數的生命週期與應用程式一致。
  • 監聽器
    在java程式設計中,我們都需要和監聽器打交道,通常一個應用當中會用到很多監聽器,我們會呼叫一個控制元件的諸如addXXXListener()等方法來增加監聽器,但往往在釋放物件的時候卻沒有記住去刪除這些監聽器,從而增加了記憶體洩漏的機會。
  • 物理連線
    一些物理連線,比如資料庫連線和網路連線,除非其顯式的關閉了連線,否則是不會自動被GC 回收的。Java資料庫連線一般用DataSource.getConnection()來建立,當不再使用時必須用Close()方法來釋放,因為這些連線是獨立於JVM的。對於Resultset和Statement 物件可以不進行顯式回收,但Connection 一定要顯式回收,因為Connection在任何時候都無法自動回收,而Connection一旦回收,Resultset 和Statement物件就會立即為NULL。但是如果使用連線池,情況就不一樣了,除了要顯式地關閉連線,還必須顯式地關閉Resultset Statement物件(關閉其中一個,另外一個也會關閉),否則就會造成大量的Statement 物件無法釋放,從而引起記憶體洩漏。
  • 內部類和外部模組等的引用 內部類的引用是比較容易遺忘的一種,而且一旦沒釋放可能導致一系列的後繼類物件沒有釋放。

具體記憶體洩露問題

是本文的主要內容,作為記錄我在開發中遇到的記憶體洩露問題和提出解決方案並分析記憶體洩露發生的原理,提供給大家參考。

okhttp攔截器導致的記憶體洩露

我的一個專案中使用Retrofit作為聯網框架,其中一個需求是用檔案下載,當然檔案下載網上已經有很多開源框架,為了提升自己最好還是自己寫。
具體的過程網路上很多。你只需要知道Retrofit是okhttp的包裝框架核心功能都是在okhttp完成的,所以最後就是給OkHttp新增攔截器,監聽網路響應體回撥。

部分程式碼說明:

這是給okhttpBuilder構造器新增攔截器的程式碼,關鍵在於從外部傳入的介面listener。

OkHttpClient.Builder httpBuilder = new OkHttpClient.Builder();
 httpBuilder.addNetworkInterceptor(new Interceptor() {
            @Override
            public Response intercept(Chain chain) throws IOException {

                Response originalResponse = chain.proceed(chain.request());
                return originalResponse.newBuilder().body(new ProgressResponseBody(originalResponse.body(),listener)).build();
            }
        });

介面的例項化寫在Activity的onCreate方法中,這樣就能得到下載進度的回撥,當然回撥是發生在網路子執行緒中而不是UI執行緒。然後在相應的地方做點選事件開啟網路下載事件。

public class ImageDetailActivity extends BaseActivity{

 @Override
    protected void onCreate(Bundle savedInstanceState) {
onProgressResponseListener=new OnProgressResponseListener() {
            @Override
            public void onResponseProgress(long bytesRead, long contentLength, boolean done) {
                Logger.d("bytesRead="+bytesRead+" contentLength="+contentLength+" done"+done);
            }
        };
       }
   }

重點

重點來了,然後debug執行App跑著跑著就發生了記憶體洩露。Leaks分析完成後定位發生記憶體洩露的程式碼行。
記憶體洩露
分析:

  • 定位到的程式碼行有點多,可以肯定的是一定是在新增介面例項化程式碼後產生的問題。
  • 其實剛才就說到回撥是發生在網路子執行緒,介面的例項化之後通過工具類被加入了OkHttpClient.Builder物件的內部變數final List<Interceptor> networkInterceptors中,
  • 然後這個變數通過生成器模式構建出了OkHttpClient物件,最後Activity中例項化的onProgressResponseListener物件和OkHttpClient物件形成了引用關係,而他們的屬於不同執行緒生命週期中。
  • 當用戶按下返回鍵Activity的例項被finish()之後,OkHttpClient物件還存在,導致Activity例項不能被回收,從而產生了記憶體洩露。

總結

產生洩露的原因是兩者發生引用,而生命週期不同。
上面的程式碼只是為了說明記憶體洩露發生的情況,當時在Activity中例項化onProgressResponseListener介面是為測試攔截器是否新增成功,是測試程式碼。真正的專案使用是不能這麼寫的,完整的寫法網路上有很多,比如解決Retrofit檔案下載進度顯示問題。其中就是定義ProgressHelper類把ProgressListener介面例項化和OkHttp的構造寫在同一個類裡面,然後通過Handler傳送訊息實現UI層的回撥。

單例引起的記憶體洩露

單例使用不當也會發生記憶體洩露,比如下面的程式碼,餓漢單例模式,當呼叫setup(Context context)方法傳入某個Activity例項的,當Activity被銷燬,系統試圖回收垃圾時,就會產生記憶體洩露。

public class AppSettings {
            private Context mAppContext;
            private static AppSettings sInstance = new AppSettings();

            //some other codes
            public static AppSettings getInstance() {
                return sInstance;
            }

            public final void setup(Context context) {
                mAppContext = context;
            }
        }

分析

原因是,這個Activity例項和單例產生了引用關係,而sInstance作為靜態物件,其生命週期要長於普通的物件,其中就包括Activity例項。

解決

解決的方法就是不持有Activity的引用,而是持有Application的Context引用。因為Application的生命週期是貫穿整個App的執行期間的,這樣兩者的生命週期一致,就不會發生記憶體洩露。修改程式碼如下

public final void setup(Context context) {
    mAppContext = context.getApplicationContext(); 
}

有關記憶體洩露的總結

  • 不要讓生命週期長於Activity的物件持有到Activity的引用
  • 儘量使用Application的Context而不是Activity的Context
  • 儘量不要在Activity中使用非靜態內部類,因為非靜態內部類會隱式持有外部類例項的引用
  • 如果使用靜態內部類,將外部例項引用作為弱引用持有。

總結

  • 本文先介紹相關知識背景和記憶體檢測工具
  • 介紹容易引起記憶體洩漏的幾大原因,並舉例說明。
  • 這篇部落格作為記憶體洩露問題的總結,暫時就想到這麼多,以後遇到問題解決之後會更新博文。

相關推薦

Android記憶體洩露檢測工具實際開發遇到的記憶體洩露問題解析

介紹 記憶體洩露是平常開發中經常遇到的,有些時候稍不注意就會發生,而且還不易察覺,這就需要工具來幫助檢測。本文主要介紹記憶體檢測工具和我在開發中遇到的記憶體洩露問題和解決方案。 記憶體洩露的原理 具體的原理涉及到虛擬機器垃圾回收機制知識,這裡只為下文作

Wince記憶體洩露檢測工具Application Verifier的使用如何快速定位洩露語句(二)

通過Application Verifier在wince端生成日誌檔案後,我們將檔案拷貝到PC上通過PC端的工具進行日誌分析,我們就可以定位出洩漏地址的程式碼行位置了,這裡有個前提條件,那就是需要我們在編譯程式時將程式的除錯資訊開啟,VS2008設定選項配置方法如下: 1.

linux下記憶體洩露檢測工具Valgrind介紹

一、工作中一個記憶體洩漏問題的解決過程: 問題背景:我司裝置上執行有多個程序,在裝置執行兩天後,程序jsman所佔用的記憶體達到了1200M bytes(通過ps -aux檢視)。 解決步驟: 確定裝置上的軟體版本,根據git的commit號資訊回退

精準 iOS 記憶體洩露檢測工具----MLeaksFinder

背景 平常我們都會用 Instrument 的 Leaks / Allocations 或其他一些開源庫進行記憶體洩露的排查,但它們都存在各種問題和不便,我們逐個來看這些工具的使用和存在的問題。 Leaks 先看看 Leaks,從蘋果的開發者文件裡可以看到,一個 app 的記憶體分三類:

Visual C++ 記憶體洩露檢測工具(VLD)

簡述 C/C++ 程式越複雜,記憶體的管理顯得越重要,稍有不慎就會出現洩漏。如果記憶體洩漏不是很嚴重,在短時間內對程式不會有太大影響,這也使得記憶體洩漏問題有很強的隱蔽性,不易被發現。然而不管記憶體洩漏多麼輕微,當程式長時間執行時,其破壞力是驚人的 - 從效能

Windows/Linux下C/C++記憶體洩露檢測工具

一 Window下的記憶體洩露檢測(以VC++環境為例) 靈活自由是C語言的一大特色,但這個特色也難以避免的帶來一些副作用,比如記憶體洩露。眾所周知,記憶體洩露的問題比較複雜,程式正常執行時你看不出它有任何異常,但長時間執行或在特定條件下特定操作重複多次時,它才暴露出來。所

iOS-記憶體洩露檢測工具(MLeaksFinder)

MLeaksFinder MLeaksFinder helps you find memory leaks in your iOS apps at develop time. It can automatically find leaks in UIVi

Linux C 程式設計記憶體洩露檢測工具(二):memwatch

Memwatch簡介 在三種檢測工具當中,設定最簡單的算是memwatch,和dmalloc一樣,它能檢測未釋放的記憶體、同一段記憶體被釋放多次、位址存取錯誤及不當使用未分配之記憶體區域。請往http://www.linkdata.se/sourcecode.html下載最

Mac下C++記憶體洩露檢測工具

1) nvwa(寓意女媧補天,從sourceforge下載),直接將其程式碼編譯到工程中,發現有洩露後程序即終止; nvwa能夠檢測到記憶體洩露問題,但是試用後感覺不穩定 2) leaks命令,能確

簡單強大的Android記憶體洩漏檢測工具 LeakCanary

    幾乎每個程式設計師在開發的過程中都會遇到記憶體洩漏,那麼我們如何檢測到app是否哪裡出現記憶體洩漏呢?square公司推出了一款簡單粗暴的檢測記憶體洩漏的工具-- LeakCanary 什麼是記憶體洩漏?        記憶體洩漏是指由於疏忽或者錯誤造成程式未能釋放已經不再使用的記憶體,記憶體洩漏不

初識記憶體洩露檢測工具VisualLeakDetector

VLD為vc++下的記憶體洩露檢測工具 1、首先下載安裝vld,直接下載安裝包,安裝過程中會直接新增環境變數。 2、在安裝目錄下有vld.h, vldapi.h, vld.lib, vldmt.lib, vldmtdll.lib, dbghelp.dll等檔

記憶體洩露檢測工具比較

1.     ccmalloc-Linux和Solaris下對C和C++程式的簡單的使用記憶體洩漏和malloc除錯庫。 2.     Dmalloc-Debug Malloc Library. 3.     Electric Fence-Linux分發版中

記憶體洩露檢測工具Valgrind

記憶體洩露簡介 什麼是記憶體洩漏   記憶體洩漏(Memory Leak)是指程式中已動態分配的堆記憶體由於某種原因,程式未釋放或無法釋放,造成系統記憶體的浪費,導致程式執行速度減慢甚至系統崩潰等嚴重後果。  記憶體洩漏缺陷具有隱蔽性、積累性的特徵,比其他記憶體非法訪問錯誤更難檢測。因為記憶體洩漏的產生原

c++記憶體洩漏檢測工具(上)

原文連結: http://blog.csdn.net/beanjoy/article/details/7578372   1/  VC自帶的CRT:_CrtCheckMemory   偵錯程式和 CRT 除錯堆函式 用法 /********

android---實際開發遇到的問題總結

在實際開發專案的時候經常會遇到一些問題,在這裡進行總結,希望讀者在遇到相同問題的時候能夠儘快解決自己的問題,或者為讀者提供一種解決方案 問題1:          隨著專案的逐漸增大,不可避免

Android開發常見記憶體洩漏問題

##一、記憶體洩漏原因 當一個物件不再使用時,本該被回收,而另一個正在使用的物件持有它的引用導致不能被回收,就產生了記憶體洩漏。 ##二、記憶體洩漏的影響 Android系統為每個應用程式分配的記憶體有限,當應用中記憶體洩漏較多時,輕則造成可用空間不足,頻繁發生gc,表現為應用執行卡

C 記憶體洩漏檢測工具

所有使用動態記憶體分配(dynamic memory allocation)的程式都有機會遇上記憶體洩露(memory leakage)問題,在Linux裡有三種常用工具來檢測記憶體洩露的情況,包括: mtrace dmalloc memwatch 1. mtrace

Linux的常用記憶體問題檢測工具

C/C++等底層語言在提供強大功能及效能的同時,其靈活的記憶體訪問也帶來了各種糾結的問題。如果crash的地方正是記憶體使用錯誤的地方,說明你人品好。如果crash的地方記憶體明顯不是

Spring Boot 實踐 第六章 Spring data JAP在實際開發的封裝應用(上)

上一章簡單介紹了一下Spring boot和Spring Data JPA的整合和簡單使用.  但是在實際開發過程中, 我們發現Spring Data JPA提供的介面太簡單了,這樣就導致需要編寫大量的重複程式碼. 實際上Spring Data JPA提供了很多種擴充套件方

Unix下C程式記憶體洩漏檢測工具Valgrind安裝與使用

                Valgrind是一款用於記憶體除錯、記憶體洩漏檢測以及效能分析的軟體開發工具。 Valgrind的最初作者是Julian Seward,他於2006年由於在開發Valgrind上的工作獲得了第二屆Google-O'Reilly開原始碼獎。 Valgrind遵守GNU通用公共許