編輯:關於android開發
開始學習啦,吼吼,我很盡量寫清楚自己想的與學習到的知識,希望大家不要嫌啰嗦,仔細看下我描述與擴展的知識,一定能學到東西的!
好了,先簡單說下,大家有或經常碰到OOM的問題,對吧?很多這樣的問題只要一出現相信大家的想法跟我的一樣,就是自己的應用:優化、優化、再優化!而且如果出現類似於OOM這樣級別的問題,根本就不好處理,LogCat日志中顯示的信息僅僅是OOM,並不會給你提示如何解決的方法或思路,因為引起OOM的原因是你應用的問題,不是系統問題!應該想下,在優化之前找到需要優化的地方,再去做優化操作不是更直接嗎?相信大多數朋友應該經常聽過或使用Jnuit調試吧,好了,廢話不多說,今天我就跟大家一起來學習總結下OOM的調試方法,來找到需要優化的地方,要知道OOM也是可以一步步調試的:
首先,先一起來做些小小的知識鋪墊:
Android(Java)中常見的容易引起內存洩漏的不良代碼:
1. 查詢數據庫沒有關閉游標
程序中經常會進行查詢數據庫的操作,但是經常會有使用完畢Cursor後沒有關閉的情況。如果我們的查詢結果集比較小,對內存的消耗不容易被發現,只有在常時間大量操作的情況下才會復現內存問題,這樣就會給以後的測試和問題排查帶來困難和風險示例代如下碼:
Java代碼2. 構造Adapter時,沒有使用緩存的 convertView ,這個問題我上一篇中已經講過了,大家可以回過頭看下
3. Bitmap對象不在使用時調用recycle()沒有及時釋放
如果一個Bitmap對象比較占內存,當它不在被使用的時候,可以調用Bitmap.recycle()方法回收此對象的像素所占用的內存
4.沒有及時釋放對象的引用
簡單舉個例子:比如兩個Activity之間傳遞的Context 或其它的自定義對象,使用完後必須立即釋放 即:Activity = null ; Context = null ; Object = null;可以的話在這釋放對象之後通知系統來回收:System.gc();這樣最好了!
Android主要應用在嵌入式設備當中,而嵌入式設備由於一些眾所周知的條件限制,通常都不會有很高的配置,特別是內存是比較有限的。如果我們編寫的代 碼當中有太多的對內存使用不當的地方,難免會使得我們的設備運行緩慢,甚至是死機。為了能夠使得Android應用程序安全且快速的運行,Android 的每個應用程序都會使用一個專有的Dalvik虛擬機實例來運行,它是由Zygote服務進程演變過來的,也就是說每個應用程序都是在屬於自己的進程中運行的(問題一:這個地方我怎麼知道是在屬於自己的進程中運行的?大家繼續,答案我會在下面詳細介紹)。一方面,如果程序在運行過程中出現了內存洩漏的問題,僅僅會使得自己的進程被殺掉,而不會影響其他進程(如果是system_process 等系統進程出問題的話,則會引起系統重啟)。另一方面Android為不同類型的進程分配了不同的內存使用上限,如果應用進程使用的內存超過了這個上限, 則會被系統視為內存洩漏,從而被殺掉
下面我來解釋下問題一:”每個應用程序都是在屬於自己的進程中運行的”這句話,對於這句話,大家只記住一點:“當一個程序第一次啟動的時候,Android會啟動一個LINUX進程和一個主線程。默認的情況下,所有該程序的組件都將在該進程和線程中運行。”~^_^ O_O !!!
同時,Android會為每個應用程序分配一個單獨的LINUX用戶。Android會盡量保留一個正在運行進程,只在內存資源出現不足時,Android會嘗試停止一些進程從而釋放足夠的資源給其他新的進程使用, 也能保證用戶正在訪問的當前進程有足夠的資源去及時地響應用戶的事件。Android會根據進程中運行的組件類別以及組件的狀態來判斷該進程的重要性,Android會首先停止那些不重要的進程。按照重要性從高到低一共有五個級別就是我們常說的:前台進程、可見進程、服務進程、後台進程、空進程(此處概念略,大家自己查)
還有個小擴展:當一個程序第一次啟動時,Android會同時啟動一個對應的主線程(Main Thread),主線程主要負責處理與UI相關的事件,如用戶的按鍵事件,用戶接觸屏幕的事件以及屏幕繪圖事件,並把相關的事件分發到對應的組件進行處理。所以主線程通常又被叫做UI線程。在開發Android應用時必須遵守單線程模型的原則: Android UI操作並不是線程安全的並且這些操作必須在UI線程中執行。Android的UI是單線程(Single-threaded)的。為了避免拖住GUI,一些較費時的對象應該交給獨立的線程去執行。如果幕後的線程來執行UI對象,Android就會發出錯誤訊息 CalledFromWrongThreadException。以後遇到這樣的異常拋出時就要知道怎麼回事咯!
好了,鋪墊知識就寫這麼多了,下面直接進入主題了:OOM調試
方式一:使用內存監測工具 DDMS –> Heap:(真機、模擬器均可使用)
1. 啟動eclipse後,切換到DDMS透視圖,並確認Devices視圖、Heap視圖都是打開的,沒打開的直接Window>ShowView>自己選;
2. 將手機通過USB鏈接至電腦,鏈接時需要確認手機是處於“USB調試”模式
3. 鏈接成功後,在DDMS的Devices視圖中將會顯示手機設備的序列號,以及設備中正在運行的部分進程信息;
4. 點擊選中想要監測的進程,如果在進程列表中未出現你的進程的話隨便選中一條讓Device一排的工具處於可用狀態,再點擊下Update Heap讓其自動找到我們跑的應用的進程,比如我臨時跑的兩個應用進程如圖:
5. 點擊Heap視圖中的“Cause GC”按鈕;
6.點擊Cause GC之後就可以看到我們應用的內存情況如下圖:
說明:
a) 點擊“Cause GC”按鈕相當於向虛擬機請求了一次gc操作;
b) 當內存使用信息第一次顯示以後,無須再不斷的點擊“Cause GC”,Heap視圖界面會定時刷新,在對應用的不斷的操作過程中就可以看到內存使用的變化;
c) 內存使用信息的各項參數根據名稱即可知道其意思,不知道具體意思的朋友自行用工具(有道、詞霸查去)
知道工具使用了,那麼如何才能知道我們的程序是否有內存洩漏的可能性呢。這裡需要注意一個值:Heap視圖中部有一個Type叫做data object,即數據對象,也就是我們的程序中大量存在的類類型的對象。在data object一行中有一列是“Total Size”,其值就是當前進程中所有Java數據對象的內存總量,如果大家想要看“Total Size”是分配的具體信息可以點擊“data object這一行來查看詳細信息,如下圖”(大家看不清楚的點擊看大圖)
一般情況下,在data object行的“Total Size”這個值的大小決定了是否會有內存洩漏。可以這樣判斷:
a) 不斷的操作當前應用,同時注意觀察data object的Total Size值;
b) 正常情況下Total Size值都會穩定在一個有限的范圍內,也就是說由於程序中的的代碼良好,沒有造成對象不被垃圾回收的情況,所以說雖然我們不斷的操作會不斷的生成很多對 象,而在虛擬機不斷的進行GC的過程中,這些對象都被回收了,內存占用量會會落到一個穩定的水平;
c) 反之如果代碼中存在沒有釋放對象引用的情況,則data object的Total Size值在每次GC後不會有明顯的回落,隨著操作次數的增多Total Size的值會越來越大,
直到到達一個上限後導致進程被殺掉。
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
d) 此處以com.xiaoma.www進程為例,在我的測試環境中com.xiaoma.www進程所占用的內存的data object的Total Size正常情況下穩定在0.8~1.0M之間,而當其值超過3~5M每次啟動應用該值不穩定的時候進程就會被系統殺掉啦!
方式二:
內存監測工具 DDMS –> Heap
使用內存分析工具 MAT(Memory Analyzer Tool)
(一) 生成.hprof文件 (生成很簡單,直接點擊Device 工具欄中的 Dump HPROF file即可生成)
(二) 使用MAT導入.hprof文件
(三) 使用MAT的視圖工具分析內存
總之,使用DDMS的Heap視圖工具可以很方便的確認我們的程序是否存在內存洩漏的可能性。這個地方順帶著也簡單的說一下,如果大家想要跟蹤更詳細的內存是怎樣分配的話,可以學著使用下Windows>ShowView>Allocation Tracker工具來定位你的內存什麼時候被什麼東西占用了,是bundlea或HashMap或ArrayList焦點或是其它的什麼占用了這些都可以在這個插件中查看到的,簡單的使用方法就是:選中Device進程列表中的某一個進程,讓Allocation Tracker裡面的跟蹤工具處於可用狀態,點擊Stop Tracking來跟蹤程序,過一定時間後,點擊Get Allocations來獲取內存分配的消息就可以查看更為詳細的內存分配情況了,如下圖:
Allocation Tracker這個小工具比較簡單, 我在這個地方不一一說明了,想學的朋友們自己點擊看看就會知道裡面的參數是什麼意思了…..^_^……..最後,還是一樣,我在內存管理方面還要學的東西很多,如果文章中我有講的不清楚或不足之處,大家留言批評並提出更好的建議,我一定及時改進,在此先謝謝大家啦,吼吼學習學習!大家加油,一起進步!!!
Activity你需要知道的一切 最近想寫篇關於Activity啟動過程源碼分析的博客,在此之前先總結下Android中Activity必須要知道的一些基礎知識,以方便後
2015 Android Dev Summit(安卓開發峰會)第一天,androidsummit今年的Google I/O沒有抽到票,不能到現場參加。不過11月舉行的An
Android的SwipeToDismiss第三方開源框架模擬QQ對話列表側滑刪除,置頂,將頭像圖片圓形化處理。,android第三方側滑 《Android
linux2.4.18----25.文件系統的構建一. 文件系統的構建1.busybox的編譯方法: 用虛擬機的redhat9.0進行編譯版本: busybox-1.00