日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 >

DDMS的使用、内存溢出的调试和模拟器的启动命令参数

發(fā)布時(shí)間:2025/7/14 58 豆豆
生活随笔 收集整理的這篇文章主要介紹了 DDMS的使用、内存溢出的调试和模拟器的启动命令参数 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

DDMS 的全稱是Dalvik Debug Monitor Service,它為我們提供例如:為測試設(shè)備截屏,針對特定的進(jìn)程查看正在運(yùn)行的線程以及堆信息、Logcat、廣播狀態(tài)信息、模擬電話呼叫、接收 SMS、虛擬地理坐標(biāo)等等。DDMS為IDE和emultor及真正的android設(shè)備架起來了一座橋梁。開發(fā)人員可以通過DDMS看到目標(biāo)機(jī)器上運(yùn)行 的進(jìn)程/現(xiàn)成狀態(tài),可以android的屏幕到開發(fā)機(jī)上,可以看進(jìn)程的heap信息,可以查看logcat信息,可以查看進(jìn)程分配內(nèi)存情況,可以像目標(biāo)機(jī) 發(fā)送短信以及打電話,可以像android開發(fā)發(fā)送地理位置信息??梢韵馟DB一樣attach某一個(gè)進(jìn)程調(diào)試。 SDKàtools目錄下提供了ddms的完整版,直接雙擊ddms.bat運(yùn)行即可。下面以Eclipse的DDMS perspective為例簡單介紹DDMS的功能。

????? 跟debug,java的perspective一樣,安裝好adt后會(huì)有一個(gè)DDMS得perspective,打開即可。

????? 如果perspective里沒有顯示DDMS,剛按如下步驟執(zhí)行:

  • ????? 點(diǎn)擊上圖中DDMS圖標(biāo)左邊的那個(gè)圖標(biāo),然后在下圖中如果有DDMS,剛選擇,如果沒有,剛選擇“其他”,然后在出現(xiàn)的窗口中雙擊“DDMS”即可。

????? 注意:DDMS對Emulator和外接測試機(jī)有同等效用。如果系統(tǒng)檢測到它們(VM)同時(shí)運(yùn)行,那么DDMS將會(huì)默認(rèn)指向 Emulator。以上2種啟動(dòng)后的操作有些不一樣,建議分別嘗試下。

????? DDMS?的工作原理

????? DDMS將搭建起IDE與測試終端(Emulator 或者connected device)的鏈接,它們應(yīng)用各自獨(dú)立的端口監(jiān)聽調(diào)試器的信息,DDMS可以實(shí)時(shí)監(jiān)測到測試終端的連接情況。當(dāng)有新的測試終端連接后,DDMS將捕捉到 終端的ID,并通過adb建立調(diào)試器,從而實(shí)現(xiàn)發(fā)送指令到測試終端的目的。

????? DDMS監(jiān)聽第一個(gè)終端App進(jìn)程的端口為8600,APP進(jìn)程將分配8601,如果有更多終端或者更多APP進(jìn)程將按照這個(gè)順序依次類推。DDMS通過8700端口(“base port”)接收所有終端的指令。

????? 打開后的窗口為:

????? 下邊通過GUI詳細(xì)了解DDMS的一些功能

????? 在GUI的左上角可以看到標(biāo)簽為”Devices”的面板,這里可以查看到所有與DDMS連 接的終端的詳細(xì)信息,以及每個(gè)終端正在運(yùn)行的APP進(jìn)程,每個(gè)進(jìn)程最右邊相對應(yīng)的是與調(diào)試器鏈接的端口。因?yàn)锳ndroid是基于Linux內(nèi)核開發(fā)的操 作平臺,同時(shí)也保留了Linux中特有的進(jìn)程ID,它介于進(jìn)程名和端口號之間。

????? device窗口羅列模擬器中所有的進(jìn)程,右上角那一排按鈕分別為:調(diào)試某個(gè)進(jìn)程,更新某個(gè)進(jìn)程,更新進(jìn)程堆棧信息,停止某個(gè)進(jìn)程,最后一個(gè)圖片按鈕時(shí)抓取android目前的屏幕。

????? 當(dāng)你選中某個(gè)進(jìn)程,并按下調(diào)試進(jìn)程按鈕時(shí),如果eclipse中有這個(gè)進(jìn)程的代碼,那就可以進(jìn)行源代碼級別的調(diào)試。有點(diǎn)像GDB attach。圖片抓取按鈕可以把當(dāng)前android的顯示桌面抓到你的機(jī)器上,也是非常有用。

????? 右邊那個(gè)窗口中有threads, heap , file explorer選項(xiàng)卡。分別顯示線程統(tǒng)計(jì)信息,棧信息,以及android的文件系統(tǒng)。

????? file explorer非常有用,他可以把文件上傳到android手機(jī),或者從手機(jī)下載下來,也可以進(jìn)行刪除操作。選中file explorer選項(xiàng)卡后,按下面三個(gè)按鈕便可實(shí)現(xiàn)對android手機(jī)文件系統(tǒng)的上傳,下載,刪除操作。

????? emulator control也是非常重要的,通過它可以像手機(jī)發(fā)送短信, 打電話,已經(jīng)更新手機(jī)位置信息。

??????????? Telephony Status: 通過選項(xiàng)模擬語音質(zhì)量以及信號連接模式。?
??????????? Telephony Actions: 模擬電話接聽和發(fā)送SMS到測試終端。?
??????????? Location Control: 模擬地理坐標(biāo)或者模擬動(dòng)態(tài)的路線坐標(biāo)變化并顯示預(yù)設(shè)的地理標(biāo)識,可以通過以下3種方式:

????????????????? · Manual: 手動(dòng)為終端發(fā)送二維經(jīng)緯坐標(biāo)。

????????????????? · GPX: 通過GPX文件導(dǎo)入序列動(dòng)態(tài)變化地理坐標(biāo),從而模擬行進(jìn)中GPS變化的數(shù)值。

????????????????? · KML: 通過KML文件導(dǎo)入獨(dú)特的地理標(biāo)識,并以動(dòng)態(tài)形式根據(jù)變化的地理坐標(biāo)顯示在測試終端。

????? LogCat:顯示輸出的調(diào)試信息。

????? Console(控制臺):是Android模擬器輸出的信息,加載程序等信息;

??????總結(jié):

??????????? eclipse adt目前提供的的ddms功能只是真正ddms的一小部分,你 可以直接使用tools下面的ddms來使用所有功能。其中有一個(gè)查看進(jìn)程內(nèi)存分配的功能比較有用。

??????????? 另個(gè)要注意的是,在DDMS中模擬發(fā)送短信時(shí),中文顯示為亂碼,在未來的開發(fā)中,我們必須要注意中文字符的問題

?

?

?

Android?內(nèi)存泄漏調(diào)試

?

一、概述

????Java編程中經(jīng)常容易被忽視,但本身又十分重要的一個(gè)問題就是內(nèi)存使用的問題。Android應(yīng)用主要使用Java語言編寫,因此這個(gè)問題也同 樣會(huì)在Android開發(fā)中出現(xiàn)。本文不對Java編程問題做探討,而是對于在Android中,特別是應(yīng)用開發(fā)中的此類問題進(jìn)行整理。

????由于作者接觸Android時(shí)間并不是很長,因此如有敘述不當(dāng)之處,歡迎指正。

?

二、Android(Java)中常見的容易引起內(nèi)存泄漏的不良代碼

?

????Android主要應(yīng)用在嵌入式設(shè)備當(dāng)中,而嵌入式設(shè)備由于一些眾所周知的條件限制,通常都不會(huì)有很高的配置,特別是內(nèi)存是比較有限的。如果我們 編寫的代碼當(dāng)中有太多的對內(nèi)存使用不當(dāng)?shù)牡胤?#xff0c;難免會(huì)使得我們的設(shè)備運(yùn)行緩慢,甚至是死機(jī)。為了能夠使得Android應(yīng)用程序安全且快速的運(yùn) 行,Android的每個(gè)應(yīng)用程序都會(huì)使用一個(gè)專有的Dalvik虛擬機(jī)實(shí)例來運(yùn)行,它是由Zygote服務(wù)進(jìn)程孵化出來的,也就是說每個(gè)應(yīng)用程序都是在 屬于自己的進(jìn)程中運(yùn)行的。一方面,如果程序在運(yùn)行過程中出現(xiàn)了內(nèi)存泄漏的問題,僅僅會(huì)使得自己的進(jìn)程被kill掉,而不會(huì)影響其他進(jìn)程(如果是 system_process等系統(tǒng)進(jìn)程出問題的話,則會(huì)引起系統(tǒng)重啟)。另一方面Android為不同類型的進(jìn)程分配了不同的內(nèi)存使用上限,如果應(yīng)用進(jìn) 程使用的內(nèi)存超過了這個(gè)上限,則會(huì)被系統(tǒng)視為內(nèi)存泄漏,從而被kill掉。Android為應(yīng)用進(jìn)程分配的內(nèi)存上限如下所示:

位置:?/ANDROID_SOURCE/system/core/rootdir/init.rc?部分腳本

#?Define?the?oom_adj?values?for?the?classes?of?processes?that?can?be

#?killed?by?the?kernel.??These?are?used?in?ActivityManagerService.

????setprop?ro.FOREGROUND_APP_ADJ?0

????setprop?ro.VISIBLE_APP_ADJ?1

????setprop?ro.SECONDARY_SERVER_ADJ?2

????setprop?ro.BACKUP_APP_ADJ?2

????setprop?ro.HOME_APP_ADJ?4

????setprop?ro.HIDDEN_APP_MIN_ADJ?7

????setprop?ro.CONTENT_PROVIDER_ADJ?14

????setprop?ro.EMPTY_APP_ADJ?15

?

#?Define?the?memory?thresholds?at?which?the?above?process?classes?will

#?be?killed.??These?numbers?are?in?pages?(4k).

????setprop?ro.FOREGROUND_APP_MEM?1536

????setprop?ro.VISIBLE_APP_MEM?2048

????setprop?ro.SECONDARY_SERVER_MEM?4096

????setprop?ro.BACKUP_APP_MEM?4096

????setprop?ro.HOME_APP_MEM?4096

????setprop?ro.HIDDEN_APP_MEM?5120

????setprop?ro.CONTENT_PROVIDER_MEM?5632

????setprop?ro.EMPTY_APP_MEM?6144

?

#?Write?value?must?be?consistent?with?the?above?properties.

#?Note?that?the?driver?only?supports?6?slots,?so?we?have?HOME_APP?at?the

#?same?memory?level?as?services.

????write?/sys/module/lowmemorykiller/parameters/adj?0,1,2,7,14,15

?

????write?/proc/sys/vm/overcommit_memory?1

????write?/proc/sys/vm/min_free_order_shift?4

????write?/sys/module/lowmemorykiller/parameters/minfree?1536,2048,4096,5120,5632,6144

?

????#?Set?init?its?forked?children's?oom_adj.

????write?/proc/1/oom_adj?-16

?

????正因?yàn)槲覀兊膽?yīng)用程序能夠使用的內(nèi)存有限,所以在編寫代碼的時(shí)候需要特別注意內(nèi)存使用問題。如下是一些常見的內(nèi)存使用不當(dāng)?shù)那闆r。

?

(一)?查詢數(shù)據(jù)庫沒有關(guān)閉游標(biāo)

描述:

????程序中經(jīng)常會(huì)進(jìn)行查詢數(shù)據(jù)庫的操作,但是經(jīng)常會(huì)有使用完畢Cursor后沒有關(guān)閉的情況。如果我們的查詢結(jié)果集比較小,對內(nèi)存的消耗不容易被發(fā)現(xiàn),只有在常時(shí)間大量操作的情況下才會(huì)復(fù)現(xiàn)內(nèi)存問題,這樣就會(huì)給以后的測試和問題排查帶來困難和風(fēng)險(xiǎn)。

?

示例代碼:

Cursor?cursor?=?getContentResolver().query(uri?...);

if?(cursor.moveToNext())?{

????...?...?

}

?

修正示例代碼:

Cursor?cursor?=?null;

try?{

????cursor?=?getContentResolver().query(uri?...);

????if?(cursor?!=?null?&&?cursor.moveToNext())?{

????????...?...?

????}

}?finally?{

????if?(cursor?!=?null)?{

????????try?{?

????????????cursor.close();

????????}?catch?(Exception?e)?{

????????????//ignore?this

????????}

????}

}?

?

(二)?構(gòu)造Adapter時(shí),沒有使用緩存的?convertView

?

描述:

????以構(gòu)造ListView的BaseAdapter為例,在BaseAdapter中提高了方法:

public?View?getView(int?position,?View?convertView,?ViewGroup?parent)

來向ListView提供每一個(gè)item所需要的view對象。初始時(shí)ListView會(huì)從BaseAdapter中根據(jù)當(dāng)前的屏幕布局實(shí)例化一定數(shù)量的 view對象,同時(shí)ListView會(huì)將這些view對象緩存起來。當(dāng)向上滾動(dòng)ListView時(shí),原先位于最上面的list?item的view對象會(huì) 被回收,然后被用來構(gòu)造新出現(xiàn)的最下面的list?item。這個(gè)構(gòu)造過程就是由getView()方法完成的,getView()的第二個(gè)形 參?View?convertView就是被緩存起來的list?item的view對象(初始化時(shí)緩存中沒有view對象則convertView是 null)。

????由此可以看出,如果我們不去使用convertView,而是每次都在getView()中重新實(shí)例化一個(gè)View對象的話,即浪費(fèi)資源也浪費(fèi)時(shí)間,也會(huì)使得內(nèi)存占用越來越大。ListView回收list?item的view對象的過程可以查看:

android.widget.AbsListView.java?-->?void?addScrapView(View?scrap)?方法。

?

示例代碼:

public?View?getView(int?position,?View?convertView,?ViewGroup?parent)?{

????View?view?=?new?Xxx(...);

????...?...

????return?view;

}

?

修正示例代碼:

public?View?getView(int?position,?View?convertView,?ViewGroup?parent)?{

????View?view?=?null;

????if?(convertView?!=?null)?{

????????view?=?convertView;

????????populate(view,?getItem(position));

????????...

????}?else?{

????????view?=?new?Xxx(...);

????????...

????}

????return?view;

}?

?

(三)?Bitmap對象不在使用時(shí)調(diào)用recycle()釋放內(nèi)存

?

描述:

????有時(shí)我們會(huì)手工的操作Bitmap對象,如果一個(gè)Bitmap對象比較占內(nèi)存,當(dāng)它不在被使用的時(shí)候,可以調(diào)用Bitmap.recycle()方法回收此對象的像素所占用的內(nèi)存,但這不是必須的,視情況而定??梢钥匆幌麓a中的注釋:

????/**

?????*?Free?up?the?memory?associated?with?this?bitmap's?pixels,?and?mark?the

?????*?bitmap?as?"dead",?meaning?it?will?throw?an?exception?if?getPixels()?or

?????*?setPixels()?is?called,?and?will?draw?nothing.?This?operation?cannot?be

?????*?reversed,?so?it?should?only?be?called?if?you?are?sure?there?are?no

?????*?further?uses?for?the?bitmap.?This?is?an?advanced?call,?and?normally?need

?????*?not?be?called,?since?the?normal?GC?process?will?free?up?this?memory?when

?????*?there?are?no?more?references?to?this?bitmap.

?????*/

?

(四)?釋放對象的引用

?

描述:

????這種情況描述起來比較麻煩,舉兩個(gè)例子進(jìn)行說明。

示例A:

假設(shè)有如下操作

public?class?DemoActivity?extends?Activity?{

????...?...

????private?Handler?mHandler?=?...

????private?Object?obj;

????public?void?operation()?{

?????obj?=?initObj();

?????...

?????[Mark]

?????mHandler.post(new?Runnable()?{

????????????public?void?run()?{

?????????????useObj(obj);

????????????}

?????});

????}

}

????我們有一個(gè)成員變量?obj,在operation()中我們希望能夠?qū)⑻幚韔bj實(shí)例的操作post到某個(gè)線程的MessageQueue中。 在以上的代碼中,即便是mHandler所在的線程使用完了obj所引用的對象,但這個(gè)對象仍然不會(huì)被垃圾回收掉,因?yàn)镈emoActivity.obj 還保有這個(gè)對象的引用。所以如果在DemoActivity中不再使用這個(gè)對象了,可以在[Mark]的位置釋放對象的引用,而代碼可以修改為:

...?...

public?void?operation()?{

????obj?=?initObj();

????...

????final?Object?o?=?obj;

????obj?=?null;

????mHandler.post(new?Runnable()?{

????????public?void?run()?{

????????????useObj(o);

????????}

????}

}

...?...

?

示例B:

????假設(shè)我們希望在鎖屏界面(LockScreen)中,監(jiān)聽系統(tǒng)中的電話服務(wù)以獲取一些信息(如信號強(qiáng)度等),則可以在LockScreen中定義 一個(gè)PhoneStateListener的對象,同時(shí)將它注冊到TelephonyManager服務(wù)中。對于LockScreen對象,當(dāng)需要顯示鎖 屏界面的時(shí)候就會(huì)創(chuàng)建一個(gè)LockScreen對象,而當(dāng)鎖屏界面消失的時(shí)候LockScreen對象就會(huì)被釋放掉。

????但是如果在釋放LockScreen對象的時(shí)候忘記取消我們之前注冊的PhoneStateListener對象,則會(huì)導(dǎo)致LockScreen 無法被垃圾回收。如果不斷的使鎖屏界面顯示和消失,則最終會(huì)由于大量的LockScreen對象沒有辦法被回收而引起OutOfMemory,使得 system_process進(jìn)程掛掉。

????總之當(dāng)一個(gè)生命周期較短的對象A,被一個(gè)生命周期較長的對象B保有其引用的情況下,在A的生命周期結(jié)束時(shí),要在B中清除掉對A的引用。

?

(五)?其他

?

????Android應(yīng)用程序中最典型的需要注意釋放資源的情況是在Activity的生命周期中,在onPause()、onStop()、 onDestroy()方法中需要適當(dāng)?shù)尼尫刨Y源的情況。由于此情況很基礎(chǔ),在此不詳細(xì)說明,具體可以查看官方文檔對Activity生命周期的介紹,以 明確何時(shí)應(yīng)該釋放哪些資源。

?

?

三、內(nèi)存監(jiān)測工具?DDMS?-->?Heap

?

????無論怎么小心,想完全避免bad?code是不可能的,此時(shí)就需要一些工具來幫助我們檢查代碼中是否存在會(huì)造成內(nèi)存泄漏的地方。 Android?tools中的DDMS就帶有一個(gè)很不錯(cuò)的內(nèi)存監(jiān)測工具Heap(這里我使用eclipse的ADT插件,并以真機(jī)為例,在模擬器中的情 況類似)。用Heap監(jiān)測應(yīng)用進(jìn)程使用內(nèi)存情況的步驟如下:

1.?啟動(dòng)eclipse后,切換到DDMS透視圖,并確認(rèn)Devices視圖、Heap視圖都是打開的;

2.?將手機(jī)通過USB鏈接至電腦,鏈接時(shí)需要確認(rèn)手機(jī)是處于“USB調(diào)試”模式,而不是作為“Mass?Storage”;

3.?鏈接成功后,在DDMS的Devices視圖中將會(huì)顯示手機(jī)設(shè)備的序列號,以及設(shè)備中正在運(yùn)行的部分進(jìn)程信息;

4.?點(diǎn)擊選中想要監(jiān)測的進(jìn)程,比如system_process進(jìn)程;

5.?點(diǎn)擊選中Devices視圖界面中最上方一排圖標(biāo)中的“Update?Heap”圖標(biāo);

6.?點(diǎn)擊Heap視圖中的“Cause?GC”按鈕;

7.?此時(shí)在Heap視圖中就會(huì)看到當(dāng)前選中的進(jìn)程的內(nèi)存使用量的詳細(xì)情況[如圖所示]。

?

?

說明:

a)?點(diǎn)擊“Cause?GC”按鈕相當(dāng)于向虛擬機(jī)請求了一次gc操作;

b)?當(dāng)內(nèi)存使用信息第一次顯示以后,無須再不斷的點(diǎn)擊“Cause?GC”,Heap視圖界面會(huì)定時(shí)刷新,在對應(yīng)用的不斷的操作過程中就可以看到內(nèi)存使用的變化;

c)?內(nèi)存使用信息的各項(xiàng)參數(shù)根據(jù)名稱即可知道其意思,在此不再贅述。

?

????如何才能知道我們的程序是否有內(nèi)存泄漏的可能性呢。這里需要注意一個(gè)值:Heap視圖中部有一個(gè)Type叫做data?object,即數(shù)據(jù)對 象,也就是我們的程序中大量存在的類類型的對象。在data?object一行中有一列是“Total?Size”,其值就是當(dāng)前進(jìn)程中所有Java數(shù)據(jù) 對象的內(nèi)存總量,一般情況下,這個(gè)值的大小決定了是否會(huì)有內(nèi)存泄漏??梢赃@樣判斷:

a)?不斷的操作當(dāng)前應(yīng)用,同時(shí)注意觀察data?object的Total?Size值;

b)?正常情況下Total?Size值都會(huì)穩(wěn)定在一個(gè)有限的范圍內(nèi),也就是說由于程序中的的代碼良好,沒有造成對象不被垃圾回收的情況,所以說雖然我們 不斷的操作會(huì)不斷的生成很多對象,而在虛擬機(jī)不斷的進(jìn)行GC的過程中,這些對象都被回收了,內(nèi)存占用量會(huì)會(huì)落到一個(gè)穩(wěn)定的水平;

c)?反之如果代碼中存在沒有釋放對象引用的情況,則data?object的Total?Size值在每次GC后不會(huì)有明顯的回落,隨著操作次數(shù)的增多Total?Size的值會(huì)越來越大,

????直到到達(dá)一個(gè)上限后導(dǎo)致進(jìn)程被kill掉。

d)?此處已system_process進(jìn)程為例,在我的測試環(huán)境中system_process進(jìn)程所占用的內(nèi)存的data?object的Total?Size正常情況下會(huì)穩(wěn)定在2.2~2.8之間,而當(dāng)其值超過3.55后進(jìn)程就會(huì)被kill。

?

????總之,使用DDMS的Heap視圖工具可以很方便的確認(rèn)我們的程序是否存在內(nèi)存泄漏的可能性。

?

四、內(nèi)存分析工具?MAT(Memory?Analyzer?Tool)

????如果使用DDMS確實(shí)發(fā)現(xiàn)了我們的程序中存在內(nèi)存泄漏,那又如何定位到具體出現(xiàn)問題的代碼片段,最終找到問題所在呢?如果從頭到尾的分析代碼邏 輯,那肯定會(huì)把人逼瘋,特別是在維護(hù)別人寫的代碼的時(shí)候。這里介紹一個(gè)極好的內(nèi)存分析工具?--?Memory?Analyzer?Tool(MAT)。

????MAT是一個(gè)Eclipse插件,同時(shí)也有單獨(dú)的RCP客戶端。官方下載地址、MAT介紹和詳細(xì)的使用教程請參 見:www.eclipse.org/mat,在此不進(jìn)行說明了。另外在MAT安裝后的幫助文檔里也有完備的使用教程。在此僅舉例說明其使用方法。我自己 使用的是MAT的eclipse插件,使用插件要比RCP稍微方便一些。

?

????使用MAT進(jìn)行內(nèi)存分析需要幾個(gè)步驟,包括:生成.hprof文件、打開MAT并導(dǎo)入.hprof文件、使用MAT的視圖工具分析內(nèi)存。以下詳細(xì)介紹。

?

(一)?生成.hprof文件

?

????生成.hprof文件的方法有很多,而且Android的不同版本中生成.hprof的方式也稍有差別,我使用的版本的是2.1,各個(gè)版本中生成.prof文件的方法請參考:

http://android.git.kernel.org/?p=platform/dalvik.git;a=blob_plain;f=docs/heap-profiling.html;hb=HEAD。

1.?打開eclipse并切換到DDMS透視圖,同時(shí)確認(rèn)Devices、Heap和logcat視圖已經(jīng)打開了;

2.?將手機(jī)設(shè)備鏈接到電腦,并確保使用“USB?調(diào)試”模式鏈接,而不是“Mass?Storage“模式;

3.?鏈接成功后在Devices視圖中就會(huì)看到設(shè)備的序列號,和設(shè)備中正在運(yùn)行的部分進(jìn)程;

4.?點(diǎn)擊選中想要分析的應(yīng)用的進(jìn)程,在Devices視圖上方的一行圖標(biāo)按鈕中,同時(shí)選中“Update?Heap”和“Dump?HPROF?file”兩個(gè)按鈕;

5.?這是DDMS工具將會(huì)自動(dòng)生成當(dāng)前選中進(jìn)程的.hprof文件,并將其進(jìn)行轉(zhuǎn)換后存放在sdcard當(dāng)中,如果你已經(jīng)安裝了MAT插件,那么此時(shí)MAT將會(huì)自動(dòng)被啟用,并開始對.hprof文件進(jìn)行分析;

????注意:第4步和第5步能夠正常使用前提是我們需要有sdcard,并且當(dāng)前進(jìn)程有向sdcard中寫入的權(quán)限(WRITE_EXTERNAL_STORAGE),否則.hprof文件不會(huì)被生成,在logcat中會(huì)顯示諸如

?????ERROR/dalvikvm(8574):?hprof:?can't?open?/sdcard/com.xxx.hprof-hptemp:?Permission?denied.?

????的信息。

???

????如果我們沒有sdcard,或者當(dāng)前進(jìn)程沒有向sdcard寫入的權(quán)限(如system_process),那我們可以這樣做:

6.?在當(dāng)前程序中,例如framework中某些代碼中,可以使用android.os.Debug中的:

???public?static?void?dumpHprofData(String?fileName)?throws?IOException

???方法,手動(dòng)的指定.hprof文件的生成位置。例如:

???xxxButton.setOnClickListener(new?View.OnClickListener()?{

???????public?void?onClick(View?view)?{

??????????android.os.Debug.dumpHprofData("/data/temp/myapp.hprof");

??????????...?...

???????}

???}

????上述代碼意圖是希望在xxxButton被點(diǎn)擊的時(shí)候開始抓取內(nèi)存使用信息,并保存在我們指定的位置:/data/temp /myapp.hprof,這樣就沒有權(quán)限的限制了,而且也無須用sdcard。但要保證/data/temp目錄是存在的。這個(gè)路徑可以自己定義,當(dāng)然 也可以寫成sdcard當(dāng)中的某個(gè)路徑。

?

(二)?使用MAT導(dǎo)入.hprof文件

?

1.?如果是eclipse自動(dòng)生成的.hprof文件,可以使用MAT插件直接打開(可能是比較新的ADT才支持);

2.?如果eclipse自動(dòng)生成的.hprof文件不能被MAT直接打開,或者是使用android.os.Debug.dumpHprofData()方法手動(dòng)生成的.hprof文件,則需要將.hprof文件進(jìn)行轉(zhuǎn)換,轉(zhuǎn)換的方法:

????例如我將.hprof文件拷貝到PC上的/ANDROID_SDK/tools目錄下,并輸入命令hprof- conv?xxx.hprof?yyy.hprof,其中xxx.hprof為原始文件,yyy.hprof為轉(zhuǎn)換過后的文件。轉(zhuǎn)換過后的文件自動(dòng)放在 /ANDROID_SDK/tools目錄下。OK,到此為止,.hprof文件處理完畢,可以用來分析內(nèi)存泄露情況了。

3.?在Eclipse中點(diǎn)擊 Windows->Open?Perspective->Other->Memory?Analyzer,或者打 Memory?Analyzer?Tool的RCP。在MAT中點(diǎn)擊File->Open?File,瀏覽并導(dǎo)入剛剛轉(zhuǎn)換而得到的.hprof文 件。

?

(三)?使用MAT的視圖工具分析內(nèi)存

?

????導(dǎo)入.hprof文件以后,MAT會(huì)自動(dòng)解析并生成報(bào)告,點(diǎn)擊Dominator?Tree,并按Package分組,選擇自己所定義的 Package類點(diǎn)右鍵,在彈出菜單中選擇List?objects->With?incoming?references。這時(shí)會(huì)列出所有可疑 類,右鍵點(diǎn)擊某一項(xiàng),并選擇Path?to?GC?Roots?->?exclude?weak/soft?references,會(huì)進(jìn)一步篩選出 跟程序相關(guān)的所有有內(nèi)存泄露的類。據(jù)此,可以追蹤到代碼中的某一個(gè)產(chǎn)生泄露的類。

????MAT的界面如下圖所示。

?

?

????具體的分析方法在此不做說明了,因?yàn)樵贛AT的官方網(wǎng)站和客戶端的幫助文檔中有十分詳盡的介紹。

????了解MAT中各個(gè)視圖的作用很重要,例如www.eclipse.org/mat/about/screenshots.php中介紹的。

??

????總之使用MAT分析內(nèi)存查找內(nèi)存泄漏的根本思路,就是找到哪個(gè)類的對象的引用沒有被釋放,找到?jīng)]有被釋放的原因,也就可以很容易定位代碼中的哪些片段的邏輯有問題了

?

?

用 Heap監(jiān)測應(yīng)用進(jìn)程使用內(nèi)存情況的步驟如下:

1. 啟動(dòng)eclipse后,切換到DDMS透視圖,并確認(rèn)Devices視圖、Heap視圖都是打開的;
2. 將手機(jī)通過USB鏈接至電腦,鏈接時(shí)需要確認(rèn)手機(jī)是處于“USB調(diào)試”模式,而不是作為“Mass Storage”;
3. 鏈接成功后,在DDMS的Devices視圖中將會(huì)顯示手機(jī)設(shè)備的序列號,以及設(shè)備中正在運(yùn)行的部分進(jìn)程信息;
4. 點(diǎn)擊選中想要監(jiān)測的進(jìn)程,比如system_process進(jìn)程;
5. 點(diǎn)擊選中Devices視圖界面中最上方一排圖標(biāo)中的“Update Heap”圖標(biāo);
6. 點(diǎn)擊Heap視圖中的“Cause GC”按鈕;
7. 此時(shí)在Heap視圖中就會(huì)看到當(dāng)前選中的進(jìn)程的內(nèi)存使用量的詳細(xì)情況。
說明:
a) 點(diǎn)擊“Cause GC”按鈕相當(dāng)于向虛擬機(jī)請求了一次gc操作;
b) 當(dāng)內(nèi)存使用信息第一次顯示以后,無須再不斷的點(diǎn)擊“Cause GC”,Heap視圖界面會(huì)定時(shí)刷新,在對應(yīng)用的不斷的操作過程中就可以看到內(nèi)存使用的變化;
c) 內(nèi)存使用信息的各項(xiàng)參數(shù)根據(jù)名稱即可知道其意思,在此不再贅述。
??如何才能知道我們的程序是否有內(nèi)存泄漏的可能性呢。這里需要注意一個(gè)值:Heap視圖中部有一個(gè)Type叫做data object,即數(shù)據(jù)對象,也就是我們的程序中大量存在的類類型的對象。在data object一行中有一列是“Total Size”,其值就是當(dāng)前進(jìn)程中所有Java數(shù)據(jù)對象的內(nèi)存總量,一般情況下,這個(gè)值的大小決定了是否會(huì)有內(nèi)存泄漏。可以這樣判斷:
a) 不斷的操作當(dāng)前應(yīng)用,同時(shí)注意觀察data object的Total Size值;
b) 正常情況下Total Size值都會(huì)穩(wěn)定在一個(gè)有限的范圍內(nèi),也就是說由于程序中的的代碼良好,沒有造成對象不被垃圾回收的情況,所以說雖然我們不斷的操作會(huì)不斷的生成很多對 象,而在虛擬機(jī)不斷的進(jìn)行GC的過程中,這些對象都被回收了,內(nèi)存占用量會(huì)會(huì)落到一個(gè)穩(wěn)定的水平;
c) 反之如果代碼中存在沒有釋放對象引用的情況,則data object的Total Size值在每次GC后不會(huì)有明顯的回落,隨著操作次數(shù)的增多Total Size的值會(huì)越來越大,
??直到到達(dá)一個(gè)上限后導(dǎo)致進(jìn)程被kill掉。
d) 此處已system_process進(jìn)程為例,在我的測試環(huán)境中system_process進(jìn)程所占用的內(nèi)存的data object的Total Size正常情況下會(huì)穩(wěn)定在2.2~2.8之間,而當(dāng)其值超過3.55后進(jìn)程就會(huì)被kill。

?

來自:?http://apps.hi.baidu.com/share/detail/32190286

?

?


?

?

在DDMS里檢查heap的使用情況

?

Dalvik Debug Monitor Server(DDMS)是主要的Android調(diào)試工具之一,也是ADT Eclipse plug-in?的一部分,獨(dú)立的程序版本也可以在Android SDK的根目錄下的tools/下面找到。關(guān)于DDMS更多的信息,請參考使用DDMS?。

?

我們來使用DDMS檢查這個(gè)應(yīng)用的heap使用情況。你可以使用下面的兩種方法啟動(dòng)DDMS:

  • from Eclipse: click?Window > Open Perspective > Other... > DDMS
  • or from the command line: run?ddms?(or?./ddms?on Mac/Linux) in the?tools/?directory

?

在左邊的面板選擇進(jìn)程com.example.android.hcgallery,然后在 工具條上邊點(diǎn)擊Show heap updates按鈕。這個(gè)時(shí)候切換到DDMS的VM Heap分頁。它會(huì)顯示每次gc后heap內(nèi)存的一些基本數(shù)據(jù)。要看第一次gc后的數(shù)據(jù)內(nèi)容,點(diǎn)擊Cause GC按鈕:

?

?

我們可以看到現(xiàn)在的值(Allocated列)是有一些超過8MB。現(xiàn)在滑動(dòng)相片,這時(shí)看到 數(shù)據(jù)在增大。因?yàn)橹挥袃H僅13個(gè)相片在程序里邊,所以泄露的內(nèi)存只有這么大。在某種程度上來說,這時(shí)最壞的一種內(nèi)存泄露,因?yàn)槲覀儧]法得到 OutOfMemoryError來提醒我們說現(xiàn)在內(nèi)存溢出了。

?

生成heap dump

?

我們現(xiàn)在使用heap dump來追蹤這個(gè)問題。點(diǎn)擊DDMS工具條上面的Dump HPROF文件按鈕,選擇文件存儲位置,然后在運(yùn)行hprof-conv。在這個(gè)例子里我們使用獨(dú)立的MAT版本(版本1.0.1),從MAT站點(diǎn)下載?。

?

如果你使用ADT(它包含DDMS的插件)同時(shí)也在eclipse里面安裝了MAT,點(diǎn)擊“dump HPROF”按鈕將會(huì)自動(dòng)地做轉(zhuǎn)換(用hprof-conv)同時(shí)會(huì)在eclipse里面打開轉(zhuǎn)換后的hprof文件(它其實(shí)用MAT打開)。

?

用MAT分析heap dumps

啟動(dòng)MAT然后加載剛才我們生成的HPROF文件。MAT是一個(gè)強(qiáng)大的工具,講述它所有的特性超出了本文的范圍,所以我只想演示一種你可以用來檢測 泄露的方法:直方圖(Histogram)視圖。它顯示了一個(gè)可以排序的類實(shí)例的列表,內(nèi)容包括:shallow heap(所有實(shí)例的內(nèi)存使用總和),或者retained heap(所有類實(shí)例被分配的內(nèi)存總和,里面也包括他們所有引用的對象)。

?

如果我們按照shallow heap排序,我們可以看到byte[]實(shí)例在頂端。自從Android3.0(Honeycomb),Bitmap的像素?cái)?shù)據(jù)被存儲在byte數(shù)組里 (之前是被存儲在Dalvik的heap里),所以基于這個(gè)對象的大小來判斷,不用說它一定是我們泄露掉的bitmap。

?

右擊byte[]類然后選擇List Objects > with incoming references。它會(huì)生成一個(gè)heap上的所有byte數(shù)組的列表,在列表里,我們可以按照Shallow Heap的使用情況來排序。

?

選擇并展開一個(gè)比較大的對象,它將展示從根到這個(gè)對象的路徑--就是一條保證對象有效的鏈條。注意看,這個(gè)就是我們的bitmap緩存!

?

MAT不會(huì)明確告訴我們這就是泄露,因?yàn)樗膊恢肋@個(gè)東西是不是程序還需要的,只有程序員知道。在這個(gè)案例里面,緩存使用的大量的內(nèi)存會(huì)影響到后面的應(yīng)用程序,所以我們可以考慮限制緩存的大小。

?

使用MAT比較heap dumps

?

調(diào)試內(nèi)存泄露時(shí),有時(shí)候適時(shí)比較2個(gè)地方的heap狀態(tài)是很有用的。這時(shí)你就需要生成2個(gè)單獨(dú)的HPROF文件(不要忘了轉(zhuǎn)換格式)。下面是一些關(guān)于如何在MAT里比較2個(gè)heap dumps的內(nèi)容(有一點(diǎn)復(fù)雜):

  • 第一個(gè)HPROF 文件(using?File > Open Heap Dump?).
  • 打開 Histogram view.
  • 在Navigation History view里 (如果看不到就從Window > Navigation History找?), 右擊histogram?然后選擇Add to Compare Basket?.
  • 打開第二個(gè)HPROF 文件然后重做步驟2和3.
  • 切換到Compare Basket view, 然后點(diǎn)擊Compare the Results?(視圖右上角的紅色"!"圖標(biāo))。

  • ?

    • Android模擬器命令列啟動(dòng)模式
      在android-sdk-windows-1.1\tools執(zhí)行emulator以執(zhí)行模擬器
      加上-skin參數(shù),指定顯示模式為HVGA-L,則可轉(zhuǎn)為橫向
      emulator - skin HVGA-L (480*320,水平顯示)
      emulator - skin HVGA-L (320*480,垂直顯示,模擬器預(yù)設(shè)模式)
      emulator - skin HVGA-L (320*240,水平顯示)
      emulator - skin HVGA-L (240*320,垂直顯示)
    • 使用mksdcard指令模擬1GB的記憶卡
      mksdcard 1024M sacard.img
    • 模擬插入 SD 卡的模擬器
      emulator - sdcard sdcard.img
    • 使用 adb+push 上載檔案到SD記憶卡
      adb push 001.jpg /sdcard (複製檔案到 /sdcard 目錄下)
      adb push pictures /sdcard (複製 picture 照片目錄到 /sdcard 目錄下)
      adb push mp3 /sdcard (複製 mp3 音樂目錄到 /sdcard 目錄下)
      adb shell (Android 模擬器啟動(dòng)命令列模式)
      #cd /sdcard (進(jìn)入 /sdcard 目錄)
      #ls (查看 SD 記憶卡中的檔案)?
    • 使用 adb+pull 從 SD 記憶卡下載檔案
      adb pull /sdcard/001.jpg . (下載 /sdcard 目錄下的檔案)
      adb pull /sdcard/pictures . (下載 sdcard 目錄下的 pictures 目錄)
    • 刪除 SD 卡裡面的檔案
      adb shell
      #ced /sdcard
      #rm 001.jpg (刪除 SD 記憶卡裡的檔案)
      #rm -r * (刪除 SD 記憶卡裡所有檔案與目錄)
    • Android模擬器影片播放方法
      mksdcard 4096M video.img (製作一個(gè)影像檔的 SD 記憶卡)
      adb push video.avi /sdcard (從電腦複製影像檔到 SD 卡中)
      emulator -sdcard video.img (啟動(dòng)模擬器並載入 SD 卡)
      下載免費(fèi)的影片播放軟體,ex: Meridian Video Player (iiivpa.apk)
      http://sites.google.com/site/eternalsandbox/Home/meridian-video-player
      adb install iiivpa.apk (安裝Meridian Video Player)
      接下來就可以用裝上去的player播放.mp4、3gp與.wmv三種檔案格式
    • 安裝 APK 應(yīng)用程式
      adb install filename.apk (安裝filename.apk)
      adb install -r filename.apk (保留已設(shè)定資料,重新安裝filename.apk)
      adb -s emulator-5554 install filename.apk (指定安裝 APK 套件在 5554 的 Android 模擬器中)
    • 移除 APK 應(yīng)用程式
      adb uninstall package
      adb uninstall -k package (移除程式時(shí),保留資料)
      此package名稱不是安裝APK套裝時(shí)的檔名或顯示在模擬器中的應(yīng)用程式名稱
      可以先到/data/data或data/app目錄下,查詢想移除的package名稱
      adb shell
      ls /data/data 或 /data/app (查詢 Package 名稱)
      exit
      adb uninstall package (移除查詢到的 Package)
    • ADB 系統(tǒng)除錯(cuò)與連結(jié)工具
      $adb devices (顯示目前有多少個(gè)模擬器正在執(zhí)行)?
      $adb -s <serialNumber> <command> (指定模擬器來操作)
      adb -s emulator-5554 install email.apk
      $adb install apkfile (安裝 APK 應(yīng)用程式套件)
      adb install email.apk
      $adb uninstall package (移除 APK 應(yīng)用程式套件)
      adb uninstall com.android.email
      $adb shell (進(jìn)入 Android 系統(tǒng)指令列模式)
      $ls
      $dmesg (查看 Android Linux Kernel 運(yùn)作訊息)
      ls - 顯示檔案目錄
      cd - 進(jìn)入目錄
      rm - 刪除檔案
      mv - 移動(dòng)檔案
      mkdir - 產(chǎn)生目錄
      rmdir - 刪除目錄

      $adb push <file/dir> (複製檔案到 SD 卡)
      adb push mp3 /sdcard
      $adb pull <file/dir> . (從 Android 系統(tǒng)下載檔案)
      adb pull /data/app/com.android.email
      $adb logcat (監(jiān)控模擬器運(yùn)作紀(jì)錄,以Ctrl + c 離開監(jiān)控模式)
      $adb bugreport (產(chǎn)生 adb 除錯(cuò)報(bào)告)
      $adb get-state (獲得 adb 伺服器運(yùn)作狀態(tài))
      $adb start-server (啟動(dòng) adb 伺服器)
      $adb kill-server (關(guān)掉 adb 伺服器)
      $adb forward tcp:6100 tcp:7100 (更改模擬器網(wǎng)路 TCP 通訊埠)
      $adb shell ps -x (顯示 Android 上所有正在執(zhí)行的行程)
      $adb version (顯示 adb 版本)
      $adb help (顯示 adb 指令參數(shù))
    • Emulator 命令列啟動(dòng)參數(shù)
      emulator -timezone Asia/Taipei (指定時(shí)區(qū))
      emulator -no-boo-anim (省略開機(jī)小機(jī)器人動(dòng)畫畫面)
      emulator -scale auto (調(diào)整模擬器視窗大小)
      emulator - scale factor (factor: 0.1-3.0)
      emulator -dpi-device 300 (更改模擬器的解析度,default為 165dpi)
      emulator -skin <skinID> (更改模擬器顯示模式)?
      emulator -help-keys (顯示鍵盤快速鍵說明)
      emulator -shell (相當(dāng)於adb shell 功能)
      emulator -data data.img (使 /data 目錄使用 data.img 的檔案空間)
      emulator -sdcard sdcard.img (使 /sdcard 目錄使用 sdcard.img 的檔案空間)
      emulator -cache cache.img (瀏覽器暫存檔儲存空間)
      emulator -wipe-data (使模擬器恢復(fù)到原廠設(shè)定)
      emulator -help (顯示 emulator 指令參數(shù))

    轉(zhuǎn)載于:https://blog.51cto.com/shaojun168/1087143

    總結(jié)

    以上是生活随笔為你收集整理的DDMS的使用、内存溢出的调试和模拟器的启动命令参数的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。