DDMS的使用、内存溢出的调试和模拟器的启动命令参数
DDMS 的全稱是Dalvik Debug Monitor Service,它為我們提供例如:為測試設備截屏,針對特定的進程查看正在運行的線程以及堆信息、Logcat、廣播狀態信息、模擬電話呼叫、接收 SMS、虛擬地理坐標等等。DDMS為IDE和emultor及真正的android設備架起來了一座橋梁。開發人員可以通過DDMS看到目標機器上運行 的進程/現成狀態,可以android的屏幕到開發機上,可以看進程的heap信息,可以查看logcat信息,可以查看進程分配內存情況,可以像目標機 發送短信以及打電話,可以像android開發發送地理位置信息。可以像GDB一樣attach某一個進程調試。 SDKàtools目錄下提供了ddms的完整版,直接雙擊ddms.bat運行即可。下面以Eclipse的DDMS perspective為例簡單介紹DDMS的功能。
????? 跟debug,java的perspective一樣,安裝好adt后會有一個DDMS得perspective,打開即可。
????? 如果perspective里沒有顯示DDMS,剛按如下步驟執行:
- ????? 點擊上圖中DDMS圖標左邊的那個圖標,然后在下圖中如果有DDMS,剛選擇,如果沒有,剛選擇“其他”,然后在出現的窗口中雙擊“DDMS”即可。
????? 注意:DDMS對Emulator和外接測試機有同等效用。如果系統檢測到它們(VM)同時運行,那么DDMS將會默認指向 Emulator。以上2種啟動后的操作有些不一樣,建議分別嘗試下。
????? DDMS?的工作原理
????? DDMS將搭建起IDE與測試終端(Emulator 或者connected device)的鏈接,它們應用各自獨立的端口監聽調試器的信息,DDMS可以實時監測到測試終端的連接情況。當有新的測試終端連接后,DDMS將捕捉到 終端的ID,并通過adb建立調試器,從而實現發送指令到測試終端的目的。
????? DDMS監聽第一個終端App進程的端口為8600,APP進程將分配8601,如果有更多終端或者更多APP進程將按照這個順序依次類推。DDMS通過8700端口(“base port”)接收所有終端的指令。
????? 打開后的窗口為:
????? 下邊通過GUI詳細了解DDMS的一些功能
????? 在GUI的左上角可以看到標簽為”Devices”的面板,這里可以查看到所有與DDMS連 接的終端的詳細信息,以及每個終端正在運行的APP進程,每個進程最右邊相對應的是與調試器鏈接的端口。因為Android是基于Linux內核開發的操 作平臺,同時也保留了Linux中特有的進程ID,它介于進程名和端口號之間。
????? device窗口羅列模擬器中所有的進程,右上角那一排按鈕分別為:調試某個進程,更新某個進程,更新進程堆棧信息,停止某個進程,最后一個圖片按鈕時抓取android目前的屏幕。
????? 當你選中某個進程,并按下調試進程按鈕時,如果eclipse中有這個進程的代碼,那就可以進行源代碼級別的調試。有點像GDB attach。圖片抓取按鈕可以把當前android的顯示桌面抓到你的機器上,也是非常有用。
????? 右邊那個窗口中有threads, heap , file explorer選項卡。分別顯示線程統計信息,棧信息,以及android的文件系統。
????? file explorer非常有用,他可以把文件上傳到android手機,或者從手機下載下來,也可以進行刪除操作。選中file explorer選項卡后,按下面三個按鈕便可實現對android手機文件系統的上傳,下載,刪除操作。
????? emulator control也是非常重要的,通過它可以像手機發送短信, 打電話,已經更新手機位置信息。
??????????? Telephony Status: 通過選項模擬語音質量以及信號連接模式。?
??????????? Telephony Actions: 模擬電話接聽和發送SMS到測試終端。?
??????????? Location Control: 模擬地理坐標或者模擬動態的路線坐標變化并顯示預設的地理標識,可以通過以下3種方式:
????????????????? · Manual: 手動為終端發送二維經緯坐標。
????????????????? · GPX: 通過GPX文件導入序列動態變化地理坐標,從而模擬行進中GPS變化的數值。
????????????????? · KML: 通過KML文件導入獨特的地理標識,并以動態形式根據變化的地理坐標顯示在測試終端。
????? LogCat:顯示輸出的調試信息。
????? Console(控制臺):是Android模擬器輸出的信息,加載程序等信息;
??????總結:
??????????? eclipse adt目前提供的的ddms功能只是真正ddms的一小部分,你 可以直接使用tools下面的ddms來使用所有功能。其中有一個查看進程內存分配的功能比較有用。
??????????? 另個要注意的是,在DDMS中模擬發送短信時,中文顯示為亂碼,在未來的開發中,我們必須要注意中文字符的問題
?
?
?
Android?內存泄漏調試
?
一、概述
????Java編程中經常容易被忽視,但本身又十分重要的一個問題就是內存使用的問題。Android應用主要使用Java語言編寫,因此這個問題也同 樣會在Android開發中出現。本文不對Java編程問題做探討,而是對于在Android中,特別是應用開發中的此類問題進行整理。
????由于作者接觸Android時間并不是很長,因此如有敘述不當之處,歡迎指正。
?
二、Android(Java)中常見的容易引起內存泄漏的不良代碼
?
????Android主要應用在嵌入式設備當中,而嵌入式設備由于一些眾所周知的條件限制,通常都不會有很高的配置,特別是內存是比較有限的。如果我們 編寫的代碼當中有太多的對內存使用不當的地方,難免會使得我們的設備運行緩慢,甚至是死機。為了能夠使得Android應用程序安全且快速的運 行,Android的每個應用程序都會使用一個專有的Dalvik虛擬機實例來運行,它是由Zygote服務進程孵化出來的,也就是說每個應用程序都是在 屬于自己的進程中運行的。一方面,如果程序在運行過程中出現了內存泄漏的問題,僅僅會使得自己的進程被kill掉,而不會影響其他進程(如果是 system_process等系統進程出問題的話,則會引起系統重啟)。另一方面Android為不同類型的進程分配了不同的內存使用上限,如果應用進 程使用的內存超過了這個上限,則會被系統視為內存泄漏,從而被kill掉。Android為應用進程分配的內存上限如下所示:
位置:?/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
?
????正因為我們的應用程序能夠使用的內存有限,所以在編寫代碼的時候需要特別注意內存使用問題。如下是一些常見的內存使用不當的情況。
?
(一)?查詢數據庫沒有關閉游標
描述:
????程序中經常會進行查詢數據庫的操作,但是經常會有使用完畢Cursor后沒有關閉的情況。如果我們的查詢結果集比較小,對內存的消耗不容易被發現,只有在常時間大量操作的情況下才會復現內存問題,這樣就會給以后的測試和問題排查帶來困難和風險。
?
示例代碼:
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
????????}
????}
}?
?
(二)?構造Adapter時,沒有使用緩存的?convertView
?
描述:
????以構造ListView的BaseAdapter為例,在BaseAdapter中提高了方法:
public?View?getView(int?position,?View?convertView,?ViewGroup?parent)
來向ListView提供每一個item所需要的view對象。初始時ListView會從BaseAdapter中根據當前的屏幕布局實例化一定數量的 view對象,同時ListView會將這些view對象緩存起來。當向上滾動ListView時,原先位于最上面的list?item的view對象會 被回收,然后被用來構造新出現的最下面的list?item。這個構造過程就是由getView()方法完成的,getView()的第二個形 參?View?convertView就是被緩存起來的list?item的view對象(初始化時緩存中沒有view對象則convertView是 null)。
????由此可以看出,如果我們不去使用convertView,而是每次都在getView()中重新實例化一個View對象的話,即浪費資源也浪費時間,也會使得內存占用越來越大。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對象不在使用時調用recycle()釋放內存
?
描述:
????有時我們會手工的操作Bitmap對象,如果一個Bitmap對象比較占內存,當它不在被使用的時候,可以調用Bitmap.recycle()方法回收此對象的像素所占用的內存,但這不是必須的,視情況而定。可以看一下代碼中的注釋:
????/**
?????*?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.
?????*/
?
(四)?釋放對象的引用
?
描述:
????這種情況描述起來比較麻煩,舉兩個例子進行說明。
示例A:
假設有如下操作
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);
????????????}
?????});
????}
}
????我們有一個成員變量?obj,在operation()中我們希望能夠將處理obj實例的操作post到某個線程的MessageQueue中。 在以上的代碼中,即便是mHandler所在的線程使用完了obj所引用的對象,但這個對象仍然不會被垃圾回收掉,因為DemoActivity.obj 還保有這個對象的引用。所以如果在DemoActivity中不再使用這個對象了,可以在[Mark]的位置釋放對象的引用,而代碼可以修改為:
...?...
public?void?operation()?{
????obj?=?initObj();
????...
????final?Object?o?=?obj;
????obj?=?null;
????mHandler.post(new?Runnable()?{
????????public?void?run()?{
????????????useObj(o);
????????}
????}
}
...?...
?
示例B:
????假設我們希望在鎖屏界面(LockScreen)中,監聽系統中的電話服務以獲取一些信息(如信號強度等),則可以在LockScreen中定義 一個PhoneStateListener的對象,同時將它注冊到TelephonyManager服務中。對于LockScreen對象,當需要顯示鎖 屏界面的時候就會創建一個LockScreen對象,而當鎖屏界面消失的時候LockScreen對象就會被釋放掉。
????但是如果在釋放LockScreen對象的時候忘記取消我們之前注冊的PhoneStateListener對象,則會導致LockScreen 無法被垃圾回收。如果不斷的使鎖屏界面顯示和消失,則最終會由于大量的LockScreen對象沒有辦法被回收而引起OutOfMemory,使得 system_process進程掛掉。
????總之當一個生命周期較短的對象A,被一個生命周期較長的對象B保有其引用的情況下,在A的生命周期結束時,要在B中清除掉對A的引用。
?
(五)?其他
?
????Android應用程序中最典型的需要注意釋放資源的情況是在Activity的生命周期中,在onPause()、onStop()、 onDestroy()方法中需要適當的釋放資源的情況。由于此情況很基礎,在此不詳細說明,具體可以查看官方文檔對Activity生命周期的介紹,以 明確何時應該釋放哪些資源。
?
?
三、內存監測工具?DDMS?-->?Heap
?
????無論怎么小心,想完全避免bad?code是不可能的,此時就需要一些工具來幫助我們檢查代碼中是否存在會造成內存泄漏的地方。 Android?tools中的DDMS就帶有一個很不錯的內存監測工具Heap(這里我使用eclipse的ADT插件,并以真機為例,在模擬器中的情 況類似)。用Heap監測應用進程使用內存情況的步驟如下:
1.?啟動eclipse后,切換到DDMS透視圖,并確認Devices視圖、Heap視圖都是打開的;
2.?將手機通過USB鏈接至電腦,鏈接時需要確認手機是處于“USB調試”模式,而不是作為“Mass?Storage”;
3.?鏈接成功后,在DDMS的Devices視圖中將會顯示手機設備的序列號,以及設備中正在運行的部分進程信息;
4.?點擊選中想要監測的進程,比如system_process進程;
5.?點擊選中Devices視圖界面中最上方一排圖標中的“Update?Heap”圖標;
6.?點擊Heap視圖中的“Cause?GC”按鈕;
7.?此時在Heap視圖中就會看到當前選中的進程的內存使用量的詳細情況[如圖所示]。
?
?
說明:
a)?點擊“Cause?GC”按鈕相當于向虛擬機請求了一次gc操作;
b)?當內存使用信息第一次顯示以后,無須再不斷的點擊“Cause?GC”,Heap視圖界面會定時刷新,在對應用的不斷的操作過程中就可以看到內存使用的變化;
c)?內存使用信息的各項參數根據名稱即可知道其意思,在此不再贅述。
?
????如何才能知道我們的程序是否有內存泄漏的可能性呢。這里需要注意一個值:Heap視圖中部有一個Type叫做data?object,即數據對 象,也就是我們的程序中大量存在的類類型的對象。在data?object一行中有一列是“Total?Size”,其值就是當前進程中所有Java數據 對象的內存總量,一般情況下,這個值的大小決定了是否會有內存泄漏。可以這樣判斷:
a)?不斷的操作當前應用,同時注意觀察data?object的Total?Size值;
b)?正常情況下Total?Size值都會穩定在一個有限的范圍內,也就是說由于程序中的的代碼良好,沒有造成對象不被垃圾回收的情況,所以說雖然我們 不斷的操作會不斷的生成很多對象,而在虛擬機不斷的進行GC的過程中,這些對象都被回收了,內存占用量會會落到一個穩定的水平;
c)?反之如果代碼中存在沒有釋放對象引用的情況,則data?object的Total?Size值在每次GC后不會有明顯的回落,隨著操作次數的增多Total?Size的值會越來越大,
????直到到達一個上限后導致進程被kill掉。
d)?此處已system_process進程為例,在我的測試環境中system_process進程所占用的內存的data?object的Total?Size正常情況下會穩定在2.2~2.8之間,而當其值超過3.55后進程就會被kill。
?
????總之,使用DDMS的Heap視圖工具可以很方便的確認我們的程序是否存在內存泄漏的可能性。
?
四、內存分析工具?MAT(Memory?Analyzer?Tool)
????如果使用DDMS確實發現了我們的程序中存在內存泄漏,那又如何定位到具體出現問題的代碼片段,最終找到問題所在呢?如果從頭到尾的分析代碼邏 輯,那肯定會把人逼瘋,特別是在維護別人寫的代碼的時候。這里介紹一個極好的內存分析工具?--?Memory?Analyzer?Tool(MAT)。
????MAT是一個Eclipse插件,同時也有單獨的RCP客戶端。官方下載地址、MAT介紹和詳細的使用教程請參 見:www.eclipse.org/mat,在此不進行說明了。另外在MAT安裝后的幫助文檔里也有完備的使用教程。在此僅舉例說明其使用方法。我自己 使用的是MAT的eclipse插件,使用插件要比RCP稍微方便一些。
?
????使用MAT進行內存分析需要幾個步驟,包括:生成.hprof文件、打開MAT并導入.hprof文件、使用MAT的視圖工具分析內存。以下詳細介紹。
?
(一)?生成.hprof文件
?
????生成.hprof文件的方法有很多,而且Android的不同版本中生成.hprof的方式也稍有差別,我使用的版本的是2.1,各個版本中生成.prof文件的方法請參考:
http://android.git.kernel.org/?p=platform/dalvik.git;a=blob_plain;f=docs/heap-profiling.html;hb=HEAD。
1.?打開eclipse并切換到DDMS透視圖,同時確認Devices、Heap和logcat視圖已經打開了;
2.?將手機設備鏈接到電腦,并確保使用“USB?調試”模式鏈接,而不是“Mass?Storage“模式;
3.?鏈接成功后在Devices視圖中就會看到設備的序列號,和設備中正在運行的部分進程;
4.?點擊選中想要分析的應用的進程,在Devices視圖上方的一行圖標按鈕中,同時選中“Update?Heap”和“Dump?HPROF?file”兩個按鈕;
5.?這是DDMS工具將會自動生成當前選中進程的.hprof文件,并將其進行轉換后存放在sdcard當中,如果你已經安裝了MAT插件,那么此時MAT將會自動被啟用,并開始對.hprof文件進行分析;
????注意:第4步和第5步能夠正常使用前提是我們需要有sdcard,并且當前進程有向sdcard中寫入的權限(WRITE_EXTERNAL_STORAGE),否則.hprof文件不會被生成,在logcat中會顯示諸如
?????ERROR/dalvikvm(8574):?hprof:?can't?open?/sdcard/com.xxx.hprof-hptemp:?Permission?denied.?
????的信息。
???
????如果我們沒有sdcard,或者當前進程沒有向sdcard寫入的權限(如system_process),那我們可以這樣做:
6.?在當前程序中,例如framework中某些代碼中,可以使用android.os.Debug中的:
???public?static?void?dumpHprofData(String?fileName)?throws?IOException
???方法,手動的指定.hprof文件的生成位置。例如:
???xxxButton.setOnClickListener(new?View.OnClickListener()?{
???????public?void?onClick(View?view)?{
??????????android.os.Debug.dumpHprofData("/data/temp/myapp.hprof");
??????????...?...
???????}
???}
????上述代碼意圖是希望在xxxButton被點擊的時候開始抓取內存使用信息,并保存在我們指定的位置:/data/temp /myapp.hprof,這樣就沒有權限的限制了,而且也無須用sdcard。但要保證/data/temp目錄是存在的。這個路徑可以自己定義,當然 也可以寫成sdcard當中的某個路徑。
?
(二)?使用MAT導入.hprof文件
?
1.?如果是eclipse自動生成的.hprof文件,可以使用MAT插件直接打開(可能是比較新的ADT才支持);
2.?如果eclipse自動生成的.hprof文件不能被MAT直接打開,或者是使用android.os.Debug.dumpHprofData()方法手動生成的.hprof文件,則需要將.hprof文件進行轉換,轉換的方法:
????例如我將.hprof文件拷貝到PC上的/ANDROID_SDK/tools目錄下,并輸入命令hprof- conv?xxx.hprof?yyy.hprof,其中xxx.hprof為原始文件,yyy.hprof為轉換過后的文件。轉換過后的文件自動放在 /ANDROID_SDK/tools目錄下。OK,到此為止,.hprof文件處理完畢,可以用來分析內存泄露情況了。
3.?在Eclipse中點擊 Windows->Open?Perspective->Other->Memory?Analyzer,或者打 Memory?Analyzer?Tool的RCP。在MAT中點擊File->Open?File,瀏覽并導入剛剛轉換而得到的.hprof文 件。
?
(三)?使用MAT的視圖工具分析內存
?
????導入.hprof文件以后,MAT會自動解析并生成報告,點擊Dominator?Tree,并按Package分組,選擇自己所定義的 Package類點右鍵,在彈出菜單中選擇List?objects->With?incoming?references。這時會列出所有可疑 類,右鍵點擊某一項,并選擇Path?to?GC?Roots?->?exclude?weak/soft?references,會進一步篩選出 跟程序相關的所有有內存泄露的類。據此,可以追蹤到代碼中的某一個產生泄露的類。
????MAT的界面如下圖所示。
?
?
????具體的分析方法在此不做說明了,因為在MAT的官方網站和客戶端的幫助文檔中有十分詳盡的介紹。
????了解MAT中各個視圖的作用很重要,例如www.eclipse.org/mat/about/screenshots.php中介紹的。
??
????總之使用MAT分析內存查找內存泄漏的根本思路,就是找到哪個類的對象的引用沒有被釋放,找到沒有被釋放的原因,也就可以很容易定位代碼中的哪些片段的邏輯有問題了
?
?
用 Heap監測應用進程使用內存情況的步驟如下:1. 啟動eclipse后,切換到DDMS透視圖,并確認Devices視圖、Heap視圖都是打開的;
2. 將手機通過USB鏈接至電腦,鏈接時需要確認手機是處于“USB調試”模式,而不是作為“Mass Storage”;
3. 鏈接成功后,在DDMS的Devices視圖中將會顯示手機設備的序列號,以及設備中正在運行的部分進程信息;
4. 點擊選中想要監測的進程,比如system_process進程;
5. 點擊選中Devices視圖界面中最上方一排圖標中的“Update Heap”圖標;
6. 點擊Heap視圖中的“Cause GC”按鈕;
7. 此時在Heap視圖中就會看到當前選中的進程的內存使用量的詳細情況。
說明:
a) 點擊“Cause GC”按鈕相當于向虛擬機請求了一次gc操作;
b) 當內存使用信息第一次顯示以后,無須再不斷的點擊“Cause GC”,Heap視圖界面會定時刷新,在對應用的不斷的操作過程中就可以看到內存使用的變化;
c) 內存使用信息的各項參數根據名稱即可知道其意思,在此不再贅述。
??如何才能知道我們的程序是否有內存泄漏的可能性呢。這里需要注意一個值:Heap視圖中部有一個Type叫做data object,即數據對象,也就是我們的程序中大量存在的類類型的對象。在data object一行中有一列是“Total Size”,其值就是當前進程中所有Java數據對象的內存總量,一般情況下,這個值的大小決定了是否會有內存泄漏。可以這樣判斷:
a) 不斷的操作當前應用,同時注意觀察data object的Total Size值;
b) 正常情況下Total Size值都會穩定在一個有限的范圍內,也就是說由于程序中的的代碼良好,沒有造成對象不被垃圾回收的情況,所以說雖然我們不斷的操作會不斷的生成很多對 象,而在虛擬機不斷的進行GC的過程中,這些對象都被回收了,內存占用量會會落到一個穩定的水平;
c) 反之如果代碼中存在沒有釋放對象引用的情況,則data object的Total Size值在每次GC后不會有明顯的回落,隨著操作次數的增多Total Size的值會越來越大,
??直到到達一個上限后導致進程被kill掉。
d) 此處已system_process進程為例,在我的測試環境中system_process進程所占用的內存的data object的Total Size正常情況下會穩定在2.2~2.8之間,而當其值超過3.55后進程就會被kill。
?
來自:?http://apps.hi.baidu.com/share/detail/32190286
?
?
?
?
在DDMS里檢查heap的使用情況
?
Dalvik Debug Monitor Server(DDMS)是主要的Android調試工具之一,也是ADT Eclipse plug-in?的一部分,獨立的程序版本也可以在Android SDK的根目錄下的tools/下面找到。關于DDMS更多的信息,請參考使用DDMS?。
?
我們來使用DDMS檢查這個應用的heap使用情況。你可以使用下面的兩種方法啟動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
?
在左邊的面板選擇進程com.example.android.hcgallery,然后在 工具條上邊點擊Show heap updates按鈕。這個時候切換到DDMS的VM Heap分頁。它會顯示每次gc后heap內存的一些基本數據。要看第一次gc后的數據內容,點擊Cause GC按鈕:
?
?
我們可以看到現在的值(Allocated列)是有一些超過8MB。現在滑動相片,這時看到 數據在增大。因為只有僅僅13個相片在程序里邊,所以泄露的內存只有這么大。在某種程度上來說,這時最壞的一種內存泄露,因為我們沒法得到 OutOfMemoryError來提醒我們說現在內存溢出了。
?
生成heap dump
?
我們現在使用heap dump來追蹤這個問題。點擊DDMS工具條上面的Dump HPROF文件按鈕,選擇文件存儲位置,然后在運行hprof-conv。在這個例子里我們使用獨立的MAT版本(版本1.0.1),從MAT站點下載?。
?
如果你使用ADT(它包含DDMS的插件)同時也在eclipse里面安裝了MAT,點擊“dump HPROF”按鈕將會自動地做轉換(用hprof-conv)同時會在eclipse里面打開轉換后的hprof文件(它其實用MAT打開)。
?
用MAT分析heap dumps
啟動MAT然后加載剛才我們生成的HPROF文件。MAT是一個強大的工具,講述它所有的特性超出了本文的范圍,所以我只想演示一種你可以用來檢測 泄露的方法:直方圖(Histogram)視圖。它顯示了一個可以排序的類實例的列表,內容包括:shallow heap(所有實例的內存使用總和),或者retained heap(所有類實例被分配的內存總和,里面也包括他們所有引用的對象)。
?
如果我們按照shallow heap排序,我們可以看到byte[]實例在頂端。自從Android3.0(Honeycomb),Bitmap的像素數據被存儲在byte數組里 (之前是被存儲在Dalvik的heap里),所以基于這個對象的大小來判斷,不用說它一定是我們泄露掉的bitmap。
?
右擊byte[]類然后選擇List Objects > with incoming references。它會生成一個heap上的所有byte數組的列表,在列表里,我們可以按照Shallow Heap的使用情況來排序。
?
選擇并展開一個比較大的對象,它將展示從根到這個對象的路徑--就是一條保證對象有效的鏈條。注意看,這個就是我們的bitmap緩存!
?
MAT不會明確告訴我們這就是泄露,因為它也不知道這個東西是不是程序還需要的,只有程序員知道。在這個案例里面,緩存使用的大量的內存會影響到后面的應用程序,所以我們可以考慮限制緩存的大小。
?
使用MAT比較heap dumps
?
調試內存泄露時,有時候適時比較2個地方的heap狀態是很有用的。這時你就需要生成2個單獨的HPROF文件(不要忘了轉換格式)。下面是一些關于如何在MAT里比較2個heap dumps的內容(有一點復雜):
?
- Android模擬器命令列啟動模式
在android-sdk-windows-1.1\tools執行emulator以執行模擬器
加上-skin參數,指定顯示模式為HVGA-L,則可轉為橫向
emulator - skin HVGA-L (480*320,水平顯示)
emulator - skin HVGA-L (320*480,垂直顯示,模擬器預設模式)
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 模擬器啟動命令列模式)
#cd /sdcard (進入 /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 (製作一個影像檔的 SD 記憶卡)
adb push video.avi /sdcard (從電腦複製影像檔到 SD 卡中)
emulator -sdcard video.img (啟動模擬器並載入 SD 卡)
下載免費的影片播放軟體,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 應用程式
adb install filename.apk (安裝filename.apk)
adb install -r filename.apk (保留已設定資料,重新安裝filename.apk)
adb -s emulator-5554 install filename.apk (指定安裝 APK 套件在 5554 的 Android 模擬器中) - 移除 APK 應用程式
adb uninstall package
adb uninstall -k package (移除程式時,保留資料)
此package名稱不是安裝APK套裝時的檔名或顯示在模擬器中的應用程式名稱
可以先到/data/data或data/app目錄下,查詢想移除的package名稱
adb shell
ls /data/data 或 /data/app (查詢 Package 名稱)
exit
adb uninstall package (移除查詢到的 Package) - ADB 系統除錯與連結工具
$adb devices (顯示目前有多少個模擬器正在執行)?
$adb -s <serialNumber> <command> (指定模擬器來操作)
adb -s emulator-5554 install email.apk
$adb install apkfile (安裝 APK 應用程式套件)
adb install email.apk
$adb uninstall package (移除 APK 應用程式套件)
adb uninstall com.android.email
$adb shell (進入 Android 系統指令列模式)
$ls
$dmesg (查看 Android Linux Kernel 運作訊息)
ls - 顯示檔案目錄
cd - 進入目錄
rm - 刪除檔案
mv - 移動檔案
mkdir - 產生目錄
rmdir - 刪除目錄
$adb push <file/dir> (複製檔案到 SD 卡)
adb push mp3 /sdcard
$adb pull <file/dir> . (從 Android 系統下載檔案)
adb pull /data/app/com.android.email
$adb logcat (監控模擬器運作紀錄,以Ctrl + c 離開監控模式)
$adb bugreport (產生 adb 除錯報告)
$adb get-state (獲得 adb 伺服器運作狀態)
$adb start-server (啟動 adb 伺服器)
$adb kill-server (關掉 adb 伺服器)
$adb forward tcp:6100 tcp:7100 (更改模擬器網路 TCP 通訊埠)
$adb shell ps -x (顯示 Android 上所有正在執行的行程)
$adb version (顯示 adb 版本)
$adb help (顯示 adb 指令參數) - Emulator 命令列啟動參數
emulator -timezone Asia/Taipei (指定時區)
emulator -no-boo-anim (省略開機小機器人動畫畫面)
emulator -scale auto (調整模擬器視窗大小)
emulator - scale factor (factor: 0.1-3.0)
emulator -dpi-device 300 (更改模擬器的解析度,default為 165dpi)
emulator -skin <skinID> (更改模擬器顯示模式)?
emulator -help-keys (顯示鍵盤快速鍵說明)
emulator -shell (相當於adb shell 功能)
emulator -data data.img (使 /data 目錄使用 data.img 的檔案空間)
emulator -sdcard sdcard.img (使 /sdcard 目錄使用 sdcard.img 的檔案空間)
emulator -cache cache.img (瀏覽器暫存檔儲存空間)
emulator -wipe-data (使模擬器恢復到原廠設定)
emulator -help (顯示 emulator 指令參數)
轉載于:https://blog.51cto.com/shaojun168/1087143
總結
以上是生活随笔為你收集整理的DDMS的使用、内存溢出的调试和模拟器的启动命令参数的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Reading papers_15(Gr
- 下一篇: 多种分布式文件系统简介