移動端測試===Android內存泄露和GC機制(轉)


本文轉自:https://www.testwo.com/article/1153

 

1、前言

Hello,小伙伴們,相信大家在項目測試中都遇到過內存泄露問題,小編也着實爬過很多坑。比如小編所測項目,更換了多實例版本的sdk,橫豎屏切換后有MapView沒有銷毀,導致內存泄露。小編測試手表項目,因為手表內存有限,測試中常遇到應用無響應或者閃退,故而小編對GC機制進行了進一步學習了解。

        本文先對Android內存垃圾回收機制進行介紹,之后對分析、定位內存泄露常用的測試方法進行總結,分享給大家。

 

2、Android內存垃圾回收(GC機制)

2.1綜述

        Android 應用中默認有三個線程:“main”主線程、GC線程、和Heap線程,而且在GC線程運行的過程中,主線程會中斷執行。Java程序與C/C++等原生程序的一個不同點就是,Java虛擬機在運行Java程序的過程中,可以自動回收不再使用的對象實例,從而避免了程序員人工管理內存的繁瑣工作。如果設備是單核CPU設備,一次只能運行一個線程,因此在GC線程運行的時候,必須中斷主線程。但是如果設備上有多核CPU,即主線程可以和GC線程同時運行,在這種情況下執行GC,會不會中斷主線程呢?答案是會的。

        雖然有不同的內存垃圾回收實現算法,但有些算法需要中斷其他Java線程的執行,如果中斷的時間過長,給用戶的感覺就是應用的響應速度變的越來越慢,甚至有可能出現ANR錯誤。

2.2Android內存管理原理

2.2.1垃圾內存回收算法

    常見的垃圾回收算法引用計數法、標注並清理、拷貝、逐代回收,其中android系統采用的是標注並清理和拷貝GC,並不是大多數JVM實現中采用的逐代回收算法。在很多垃圾回收實現中,常常可以看到將幾種算法合並使用的場景。

 

2.2.2Logcat中的GC信息

 

Logcat中GC輸出的信息格式如下:

Dalvik虛擬機的Log信息

在Davlik虛擬機(非ART)中,每一次垃圾回收都會返回一條類似的信息。例子如下:

 

D/dalvikvm( 9050): GC_CONCURRENT freed2049K, 65% free 3571K/9991K, external 4703K/5261K, paused 2ms+2ms

它們大致可以分成如下幾個部分:

 

D/dalvikvm: <GC_Reason> <Amount_freed>, <Heap_stats>, <External_memory_stats>, <Pause_time>

[GC的原因][回收的內存總量][GC堆內存的統計信息][外部內存的統計信息][中斷時間]

各個字段的含義如下:

 
        

(1)、GC的原因,也就是GC類別,可分為:

  1.  GC_FOR_MALLOC 表示內存垃圾回收過程是因為在分配內存空間如(創建對象)時,內存不夠而引發的。系統會殺死應用的進程並且回收所有內存。

  2.    GC_CONCURRENT 表明GC是在內存使用率達到一定的警戒線時,自動觸發的。

  3. GC_EXPLICIT 表明GC是被顯式請求觸發的。

  4. GC_EXTERNAL_ALLOC在API版本10(Android3.0)以下的時候的垃圾回收機制。3.0以上版本所有的內存都在Dalvik堆中分配。它是用來回收dalvik虛擬機以外的內存(例如Bitmap中的內存或者NIObuffer中的內存)。

  5.  GC_HPROF_DUMP_HEAP 當請求生成HPROF文件來分析內存的時候會觸發此類垃圾回收

(2)、回收的內存總量

(3)、回收后GC內存對的統計信息

堆中可用空間所占的百分比和(堆中對象的數量)/(堆的大小)

(4)、外部內存的統計信息

系統API版本10以下的系統中,Dalvik虛擬機堆外(分配的內存)/(限制的內存量)

 

(5)、GC造成的應用其他線程中斷的時間

Concurrent類型的垃圾回收有兩次暫停時間:一次發生在開始,另一次發生在結束。堆的內容越多,暫停的時間越長。

觀察這些Log信息,如果heapstats中的數值(堆中對象數量)/(堆的大小)越來越大,那么應用中很有可能存在內存泄漏。

(6)、總結

一般根據下面兩個線索判斷應用是否存在內存泄露問題

1、應用運行一段時間后,因為內部拋出java.lang.OutOfMemoryError異常而崩潰;

2、在logcat中看到頻繁的GC信息;

 

 

3、內存泄露

3.1什么是內存泄漏

 

 

對於不同的語言平台來說,進行標記回收內存的算法是不一樣的,像Android(Java)則采用GC-Root的標記回收算法。下圖(Google 2011的IO大會)展示了Android內存的回收管理策略。

 

圖中的每個圓節點代表對象的內存資源,箭頭代表可達路徑。當圓節點與GC Roots存在可達路徑時,表示當前資源正被引用,虛擬機是無法對其進行回收的(如圖中的黃色節點)。反過來,如果圓節點與GC Roots不存在可達路徑,則意味着這塊對象的內存資源不再被程序引用,系統虛擬機可以在GC過程中將其回收掉。

        有了上面的內存回收的栗子,那么接下來就可以說說什么是內存泄漏了。

 

        從定義上講,Android(Java)平台的內存泄漏是指沒有用的對象資源任與GC-Root保持可達路徑,導致系統無法進行回收。舉一個最簡單的栗子,我們在Activity的onCreate函數中注冊一個廣播接收者,但是在onDestory函數中並沒有執行反注冊,當Activity被finish掉時,Activity對象已經走完了自身的生命周期,應該被資源回收釋放掉,但由於沒有反注冊,此時Activity和GC-Root間仍然有可達路徑存在,導致Activity雖然被銷毀,但是所占用的內存資源卻無法被回收掉。

 

 

3.2泄漏的源頭

     這里,將其歸位以下三類:

    (1).   自身編碼引起

    由項目開發人員自身的編碼造成。

    (2).   第三方代碼引起

    這里的第三方代碼包含兩類:第三方非開源的SDK和開源的第三方框架。

    (3).   系統原因

    由Android系統自身造成的泄漏,如像WebView、InputMethodManager等引起的問題,還有某些第三方ROM存在的問題。

3.3泄漏的定位

      內存泄漏不像閃退的BUG,排查起來相對要比較困難些,比較極端的情況是當應用OOM了才發現存在內存泄漏問題,對用戶影響太大。為此,我們希望在測試過程中能夠盡早發現問題。下面介紹幾種分析內存泄露問題的工具、方法。

3.3.1靜態代碼分析工具 —— Lint

        Lint是 Android Studio自帶的工具,使用姿勢很簡單Analyze -> Inspect Code然后選擇想要掃面的區域即可。

 對可能引起泄漏的編碼,Lint都會進行溫馨提示。

 

   關於Lint,大家可以自行拓展學習。

 

3.3.2Android Memory Monitor

        Android Studio提供的工具,用於監控應用的內存使用狀態,在開發中也是非常實用的工具,可以用來打印出內存的狀態信息。


 

 打印獲得的內存信息如下,可以通過右上角的綠色三角形按鈕去分析泄漏的Activity和一些重復的字符串,目前只支持這兩個,希望Google后面能夠加入更多可選分析規則。


3.3.3adb shell 命令

      使用 adb shell dumpsys meminfo [PackageName],可以打印出指定包名的應用內存信息。


  使用該命令可以很直觀的觀察到Activity的泄漏問題,是平常分析比較常用的一種方式。除了使用命令外,Android Studio也提供了下面的功能,和使用命令是一樣效果的。

 

以上就是我在做內存泄漏定位分析的時候會用到的工具和方法,通常都是結合起來用,使用多個工具互補分析問題可以提高我們的效率和最終取得的效果。


以上,就是小編做內存泄漏分析的一些心得總結,如有錯誤和不足,還請大家指出。如果大家針對內存泄露測試、分析,還有什么建議或者心得,歡迎留言、一起探討~~O(∩_∩)O~~


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM