PID Vss Rss Pss Uss cmdline
......
2319 42068K 42032K 13536K 7028K com.xxx
......
該命令可以列出當前系統(tǒng)所有進程的內(nèi)存占用情況。
PID是進程ID。
Vss是占用的虛擬內(nèi)存,如果沒有映射實際的內(nèi)存也算進來。
Rss是占用的物理內(nèi)存。是共享內(nèi)存+私有內(nèi)存。因為共享內(nèi)存是多個進程共用的,所以存在重復計算。
Pss是占用的私有內(nèi)存加上平分的共享內(nèi)存。例如一塊1M的共享內(nèi)存被兩個進程共享,那每個進程分500K。各進程的Pss相加基本等于實際被使用的物理內(nèi)存,所以這個經(jīng)常是最重要的參數(shù)。
Uss是私有內(nèi)存。
cmdline可以看做是apk包名。
通過procrank,只能很宏觀的橫向比較不同的應用。如果要更細致的了解具體內(nèi)存是如何使用,則需要進入
命令“adb shell dumpsys meminfo package.name”。在4.0 ICS(或者3.0 HoneyComb)之后的系統(tǒng)上,會看到類似下面的輸出
Shared Private Heap Heap Heap
Pss Dirty Dirty Size Alloc Free
------ ------ ------ ------ ------ ------
Native 16 8 16 3416 3300 79
Dalvik 3884 10592 3580 9560 9022 538
Cursor 0 0 0
Ashmem 0 0 0
Other dev 5110 10244 0
.so mmap 640 1948 396
.jar mmap 0 0 0
.apk mmap 68 0 0
.ttf mmap 817 0 0
.dex mmap 411 0 0
Other mmap 55 16 32
Unknown 2404 660 2388
TOTAL 13405 23468 6412 12976 12322 617
(如果使用2.3或之前的版本,結(jié)果會粗糙一些,很多都被歸入了Other,但基本結(jié)構(gòu)是一樣的)
stacktrace上有個經(jīng)常被搜到的帖子對這個格式有說明,雖然針對的是android 2.3格式,但讀后非常有收獲。
但仍有很多疑問沒有解答,例如針對上面的例子,為什么Native heap size那么大,但Pss卻那么小?占用內(nèi)存比較多的Other dev是什么?Unknown又有哪些?等等。
要理解這些,需要知道這個report是如何生成的。實際上,生成report的代碼是android的android_os_Debug.cpp。
從中我們可以發(fā)現(xiàn),上面列表的數(shù)據(jù)是由三種方式獲取的:
1. Pss/Shared Dirty/Private Dirty三列是讀取了/proc/process-id/smaps文件獲取的。它會對每個虛擬內(nèi)存塊進行解析,然后生成數(shù)據(jù)。
2. Native Heap Size/Alloc/Free三列是使用C函數(shù)mallinfo得到的。
3. Dalvik Heap Size/Alloc/Free并非該cpp文件產(chǎn)生,而是android的Debug類生成。
后面兩個Heap的獲取比較簡單,我唯一的疑惑是為什么有free的?我的理解是無論是c的malloc還是java的new,最后都是通過mmap系統(tǒng)調(diào)用進行內(nèi)存分配的。而mmap必須以頁的4K為單位。所以如果一次一次只需要malloc 2K,則剩下的2K是free的。如果下次再malloc 2K,可以仍然使用上次mmap剩余的2K內(nèi)存。
至于smaps文件,我們可以通過adb shell cat /proc/process-id/smaps來查看(需要root)。這是個普通的linux文件,描述了進程的虛擬內(nèi)存區(qū)域(vm area)的具體信息。每次mmap一般都會生成一個vm area。
在Android上,一個更加方便的命令是adb shell showmap -a process-id。
該命令也是讀取smaps文件,但結(jié)果細化的具體的vm area。
該命令輸出的每行表示一個vm area,列出了該vm area的start addr, end addr, Vss, Rss, Pss, shared clean, shared dirty, private clean, private dirty,object。
第二層的dumpsys meminfo其實就是讀取這些數(shù)據(jù),然后分類(native, dalvik, .so map, etc.)統(tǒng)計生成。
start addr和end addr表示進程空間的起止虛擬地址。
Vss,Rss,Pss跟前面說的一樣。
Object可以看做mmap的文件名。
Shared clean,按字面意思,表示共享的干凈的數(shù)據(jù)。共享表示多個進程的虛擬地址可以都指向這塊物理空間,表示多個進程共享的so庫。為什么這里說是多個進程共享的so而不是所有的so呢?
關(guān)于so庫的加載,我一直覺得是mmap帶MAP_SHARED參數(shù),但看了memory_faq,才知道是MAP_PRIVATE。如果使用showmap命令查看vm area,會發(fā)現(xiàn)有的so的內(nèi)存都屬于Shared clean,而有的so則屬于private clean。前者一般是當前進程特有的so,而后者一般是通用的so。后來看了對mmap的各種參數(shù)的實驗(很贊實踐精神),才知道第一次以MAP_PRIVATE mmap so,內(nèi)存都是private clean的。如果另外一個進程mmap了同一個so,那該vm area就變成shared clean了。
Private clean,包括該進程私有的干凈的內(nèi)存。包括前面說的該進程獨自使用的so和進程的二進制代碼段。
Clean內(nèi)存的好處是在內(nèi)存緊張時,可以釋放物理內(nèi)存。因為是clean的,所以不需要寫回到disk,只需要下次讀取該內(nèi)存(導致缺頁錯誤)時再從disk讀入。
Private dirty,表示該進程私有的不跟disk數(shù)據(jù)一致的內(nèi)存段。例如堆(heap),棧(stack),bss段。關(guān)于bss段,因為在elf文件為了節(jié)約控件沒有賦值,所以在加載到內(nèi)存時賦值為0,于是跟disk就不一致了。在showmap結(jié)果中,會發(fā)現(xiàn)幾乎每個so都有一個顯示位[bss]的private dirty段。數(shù)據(jù)段我估計是private clean的,因為elf文件是有初值的。
Shared dirty開始我一直搞不清楚。后來看了Dalvik vm internal這個video(slides),才明白了些。對于普通的linux進程,當父進程fork子進程時,父進程的虛擬內(nèi)存區(qū)域都會”復制“一份到子進程中。這里”復制“加引號,是因為為了節(jié)省內(nèi)存,也為了減少內(nèi)存拷貝的時間,使用的是copy-on-write的方法。當子進程對private dirty的堆,棧,bss沒有修改時,則是父子進程share這份dirty(因為跟disk沒法映射)數(shù)據(jù)。如果發(fā)生改變,則會修改為private dirty。所以android有zygote進程,是所有android apps進程的父進程,在其中會加載resource等資源(下文會看到,最簡單的應該也有大概5M resource,例如圖片),這些資源都是只讀的。具體的apps繼承了這些shared dirty的數(shù)據(jù),因為不修改它們,所以也不用分配多余的內(nèi)存空間。
由于android使用的linux沒有swap分區(qū),所以dirty的數(shù)據(jù)必須常駐內(nèi)存。所以dumpsys meminfo會把private dirty和shared dirty重點列出來,這也是我們優(yōu)化內(nèi)存的重點。
現(xiàn)在可以回答一個前面提到的問題,為什么Native Heap(根據(jù)mallinfo系統(tǒng)調(diào)用得到)很大而Native Pss(根據(jù)swaps得到)很小。我覺得這是dumpsys meminfo的一個bug。根據(jù)android_os_Debug.cpp的代碼,object名字是[heap]的段被認為是native heap。這在2.3是正確的,但在4.0之后,[heap]為名字的段卻很小(只有幾K)。同時,我卻發(fā)現(xiàn)有大量的[anon]的區(qū)域。我認為anon是anonymous的縮寫。malloc一般是通過mmap來分配內(nèi)存的,而參數(shù)是MAP_ANONYMOUS。所以我覺得這些[anon]是native heap。從大小上看,現(xiàn)在這些[anon]被看做是Unkown的一部分,也跟hative heap的大小差不多。
在dumpsys meminfo結(jié)果的其他值比較大的行,.so表示映射的so庫(vm area行的object名稱包含.so字樣),.dex表示映射的.dex文件(dalvik的虛擬機二進制碼),Other dev表示映射其他的/dev的(dalvik的heap也是映射到特殊的/dev上)。加上native和dalvik的heap,下次寫如何具體分析這五項。
聯(lián)系客服