Android OOM-Heap,MAT工具檢測(cè)內(nèi)存泄露

概述

在android的開(kāi)發(fā)中,要時(shí)刻主要內(nèi)存的分配和垃圾回收,因?yàn)橄到y(tǒng)為每一個(gè)dalvik虛擬機(jī)分配的內(nèi)存是有限的,在google的G1中,分配的最大堆大小只有16M,后來(lái)的機(jī)器一般都為24M,實(shí)在是少的可憐。這樣就需要我們?cè)陂_(kāi)發(fā)過(guò)程中要時(shí)刻注意。不要因?yàn)樽约旱拇a問(wèn)題而造成OOM錯(cuò)誤。

JAVA的內(nèi)存管理

大家都知道,android應(yīng)用層是由java開(kāi)發(fā)的,android的davlik虛擬機(jī)與jvm也類似,只不過(guò)它是基于寄存器的。因此要了解android的內(nèi)存管理就必須得了解java的內(nèi)存分配和垃圾回收機(jī)制。

在java中,是通過(guò)new關(guān)鍵字來(lái)為對(duì)象分配內(nèi)存的,而內(nèi)存的釋放是由垃圾收集器(GC)來(lái)回收的,工程師在開(kāi)發(fā)的過(guò)程中,不需要顯式的去管理內(nèi)存。但是這樣有可能在不知不覺(jué)中就會(huì)浪費(fèi)了很多內(nèi)存,最終導(dǎo)致java虛擬機(jī)花費(fèi)很多時(shí)間去進(jìn)行垃圾回收,更嚴(yán)重的是造成JVM的OOM。因此,java工程師還是有必要了解JAVA的內(nèi)存分配和垃圾回收機(jī)制。

內(nèi)存結(jié)構(gòu)

上面這張圖是JVM的結(jié)構(gòu)圖,它主要四個(gè)部分組成:Class Loader子系統(tǒng)和執(zhí)行引擎,運(yùn)行時(shí)方法區(qū)和本地方法區(qū),我們主要來(lái)看下RUNTIMEDATA AREA區(qū),也就是我們常說(shuō)的JVM內(nèi)存。從圖中可以看出,RUNTIMEDATA AREA區(qū)主要由5個(gè)部分組成:

Method Area:被裝載的class的元信息存儲(chǔ)在Method Area中,它是線程共享的

Heap(堆):一個(gè)java虛擬機(jī)實(shí)例中只存在一個(gè)堆空間,存放一些對(duì)象信息,它是線程共享的

Java棧:java虛擬機(jī)直接對(duì)java棧進(jìn)行兩種操作,以幀為單位的壓棧和出棧(非線程共享)

程序計(jì)數(shù)器(非線程共享)

本地方法棧(非線程共享)

JVM的垃圾回收(GC)

JVM的垃圾原理是這樣的,它把對(duì)象分為年輕代(Young)、年老代(Tenured)、持久代(Perm),對(duì)不同生命周期的對(duì)象使用不同的垃圾回收算法。

年輕代(Young)

年輕代分為三個(gè)區(qū),一個(gè)eden區(qū),兩個(gè)Survivor區(qū)。程序中生成的大部分新的對(duì)象都在Eden區(qū)中,當(dāng)Eden區(qū)滿時(shí),還存活的對(duì)象將被復(fù)制到其中一個(gè)Survivor區(qū),當(dāng)此Survivor區(qū)的對(duì)象占用空間滿了時(shí),此區(qū)存活的對(duì)象又被復(fù)制到另外一個(gè)Survivor區(qū),當(dāng)這個(gè)Survivor區(qū)也滿了的時(shí)候,從第一個(gè)Survivor區(qū)復(fù)制過(guò)來(lái)的并且此時(shí)還存活的對(duì)象,將被復(fù)制到年老代。

年老代(Tenured)

年老代存放的是上面年輕代復(fù)制過(guò)來(lái)的對(duì)象,也就是在年輕代中還存活的對(duì)象,并且區(qū)滿了復(fù)制過(guò)來(lái)的。一般來(lái)說(shuō),年老代中的對(duì)象生命周期都比較長(zhǎng)。

持久代(Perm)

用于存放靜態(tài)的類和方法,持久代對(duì)垃圾回收沒(méi)有顯著的影響。

Android中內(nèi)存泄露監(jiān)測(cè)

在了解了JVM的內(nèi)存管理后,我們?cè)倩剡^(guò)頭來(lái)看看,在android中應(yīng)該怎樣來(lái)監(jiān)測(cè)內(nèi)存,從而看在應(yīng)用中是否存在內(nèi)存分配和垃圾回收問(wèn)題而造成內(nèi)存泄露情況。

在android中,有一個(gè)相對(duì)來(lái)說(shuō)還不錯(cuò)的工具,可以用來(lái)監(jiān)測(cè)內(nèi)存是否存在泄露情況:DDMS—Heap

使用方法比較簡(jiǎn)單:

選擇DDMS視圖,并打開(kāi)Devices視圖和Heap視圖

點(diǎn)擊選擇要監(jiān)控的進(jìn)程,比如:上圖中我選擇的是system_process

選中Devices視圖界面上的"update heap"圖標(biāo)

點(diǎn)擊Heap視圖中的"Cause GC"按鈕(相當(dāng)于向虛擬機(jī)發(fā)送了一次GC請(qǐng)求的操作)

在Heap視圖中選擇想要監(jiān)控的Type,一般我們會(huì)觀察dataobject的total size的變化,正常情況下total size的值會(huì)穩(wěn)定在一個(gè)有限的范圍內(nèi),也就說(shuō)程序中的代碼良好,沒(méi)有造成程序中的對(duì)象不被回收的情況。如果代碼中存在沒(méi)有釋放對(duì)象引用的情況,那么data object的total size在每次GC之后都不會(huì)有明顯的回落,隨著操作次數(shù)的增加而total size也在不斷的增加。(說(shuō)明:選擇好data object后,不斷的操作應(yīng)用,這樣才可以看出total size的變化)。如果totalsize確實(shí)是在不斷增加而沒(méi)有回落,說(shuō)明程序中有沒(méi)有被釋放的資源引用。那么我們應(yīng)該怎么來(lái)定位呢?

Android中內(nèi)存泄露定位

Mat(memory analyzer tools)是我們常用的用來(lái)定位內(nèi)存泄露的工具,如果你使用ADT,并且安裝了MAT的eclipse插件,你需要做的是進(jìn)入DDMS視圖的Devices視圖:

點(diǎn)擊"dump HPROF file"按鈕,然后使用MAT分析下載下來(lái)的文件。

下面列出了存在的問(wèn)題,點(diǎn)擊detail進(jìn)去,會(huì)列出詳細(xì)的,可能會(huì)存在問(wèn)題的代碼:

關(guān)于MAT的使用可以參考:

http://www.blogjava.net/rosen/archive/2010/06/13/323522.html

這位兄弟寫的比較詳細(xì)。

總結(jié)

不管是java還是android,都應(yīng)該了解內(nèi)存分配和垃圾回收機(jī)制,工程師要做到寫的代碼中沒(méi)有bad code很難,關(guān)鍵是在出現(xiàn)問(wèn)題的時(shí)候該怎么去排查。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容