概述
在android的開發(fā)中夫否,要時(shí)刻主要內(nèi)存的分配和垃圾回收奕塑,因?yàn)橄到y(tǒng)為每一個(gè)dalvik虛擬機(jī)分配的內(nèi)存是有限的械姻,在google的G1中,分配的最大堆大小只有16M恩脂,后來的機(jī)器一般都為24M帽氓,實(shí)在是少的可憐。這樣就需要我們在開發(fā)過程中要時(shí)刻注意东亦。不要因?yàn)樽约旱拇a問題而造成OOM錯(cuò)誤杏节。
JAVA的內(nèi)存管理
大家都知道,android應(yīng)用層是由java開發(fā)的典阵,android的davlik虛擬機(jī)與jvm也類似奋渔,只不過它是基于寄存器的。因此要了解android的內(nèi)存管理就必須得了解java的內(nèi)存分配和垃圾回收機(jī)制壮啊。
在java中嫉鲸,是通過new關(guān)鍵字來為對象分配內(nèi)存的,而內(nèi)存的釋放是由垃圾收集器(GC)來回收的歹啼,工程師在開發(fā)的過程中玄渗,不需要顯式的去管理內(nèi)存。但是這樣有可能在不知不覺中就會浪費(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ū)屡限,我們主要來看下RUNTIMEDATA AREA區(qū),也就是我們常說的JVM內(nèi)存炕倘。從圖中可以看出钧大,RUNTIMEDATA AREA區(qū)主要由5個(gè)部分組成:
Method Area:被裝載的class的元信息存儲在Method Area中,它是線程共享的
Heap(堆):一個(gè)java虛擬機(jī)實(shí)例中只存在一個(gè)堆空間罩旋,存放一些對象信息啊央,它是線程共享的
Java棧:java虛擬機(jī)直接對java棧進(jìn)行兩種操作眶诈,以幀為單位的壓棧和出棧(非線程共享)
程序計(jì)數(shù)器(非線程共享)
本地方法棧(非線程共享)
JVM的垃圾回收(GC)
JVM的垃圾原理是這樣的,它把對象分為年輕代(Young)劣挫、年老代(Tenured)册养、持久代(Perm)东帅,對不同生命周期的對象使用不同的垃圾回收算法压固。
年輕代(Young)
年輕代分為三個(gè)區(qū),一個(gè)eden區(qū)靠闭,兩個(gè)Survivor區(qū)帐我。程序中生成的大部分新的對象都在Eden區(qū)中,當(dāng)Eden區(qū)滿時(shí)愧膀,還存活的對象將被復(fù)制到其中一個(gè)Survivor區(qū)拦键,當(dāng)此Survivor區(qū)的對象占用空間滿了時(shí),此區(qū)存活的對象又被復(fù)制到另外一個(gè)Survivor區(qū)檩淋,當(dāng)這個(gè)Survivor區(qū)也滿了的時(shí)候芬为,從第一個(gè)Survivor區(qū)復(fù)制過來的并且此時(shí)還存活的對象,將被復(fù)制到年老代蟀悦。
年老代(Tenured)
年老代存放的是上面年輕代復(fù)制過來的對象媚朦,也就是在年輕代中還存活的對象,并且區(qū)滿了復(fù)制過來的日戈。一般來說询张,年老代中的對象生命周期都比較長。
持久代(Perm)
用于存放靜態(tài)的類和方法浙炼,持久代對垃圾回收沒有顯著的影響份氧。
Android中內(nèi)存泄露監(jiān)測
在了解了JVM的內(nèi)存管理后,我們再回過頭來看看弯屈,在android中應(yīng)該怎樣來監(jiān)測內(nèi)存蜗帜,從而看在應(yīng)用中是否存在內(nèi)存分配和垃圾回收問題而造成內(nèi)存泄露情況。
在android中资厉,有一個(gè)相對來說還不錯(cuò)的工具厅缺,可以用來監(jiān)測內(nèi)存是否存在泄露情況:DDMS—Heap
使用方法比較簡單:
選擇DDMS視圖,并打開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請求的操作)
在Heap視圖中選擇想要監(jiān)控的Type店归,一般我們會觀察dataobject的total size的變化,正常情況下total size的值會穩(wěn)定在一個(gè)有限的范圍內(nèi)酪我,也就說程序中的代碼良好消痛,沒有造成程序中的對象不被回收的情況。如果代碼中存在沒有釋放對象引用的情況都哭,那么data object的total size在每次GC之后都不會有明顯的回落秩伞,隨著操作次數(shù)的增加而total size也在不斷的增加逞带。(說明:選擇好data object后,不斷的操作應(yīng)用纱新,這樣才可以看出total size的變化)展氓。如果totalsize確實(shí)是在不斷增加而沒有回落,說明程序中有沒有被釋放的資源引用脸爱。那么我們應(yīng)該怎么來定位呢遇汞?
Android中內(nèi)存泄露定位
Mat(memory analyzer tools)是我們常用的用來定位內(nèi)存泄露的工具,如果你使用ADT簿废,并且安裝了MAT的eclipse插件空入,你需要做的是進(jìn)入DDMS視圖的Devices視圖:
點(diǎn)擊"dump HPROF file"按鈕,然后使用MAT分析下載下來的文件族檬。
下面列出了存在的問題歪赢,點(diǎn)擊detail進(jìn)去,會列出詳細(xì)的单料,可能會存在問題的代碼:
關(guān)于MAT的使用可以參考:
http://www.blogjava.net/rosen/archive/2010/06/13/323522.html
這位兄弟寫的比較詳細(xì)埋凯。
總結(jié)
不管是java還是android,都應(yīng)該了解內(nèi)存分配和垃圾回收機(jī)制扫尖,工程師要做到寫的代碼中沒有bad code很難白对,關(guān)鍵是在出現(xiàn)問題的時(shí)候該怎么去排查。