Android內存管理主要有:LowMemory Killer機制,Ashmem,PMEM/ION及Native內存和Dalvik內存管理管理和JVM垃圾回收機制。html
源碼位置drivers/staging/Android/lowmemorykiller.cjava
Android是一個多任務系統,也就是說能夠同時運行多個程序,這個你們應該很熟悉。通常來講,啓動運行一個程序是有必定的時間開銷的,所以爲了 加快運行速度,當你退出一個程序時,Android並不會當即殺掉它,這樣下次再運行該程序時,能夠很快的啓動。隨着系統中保留的程序愈來愈多,內存確定 會出現不足,low memory killer就是在系統內存低於某值時,清除相關的程序,保障系統保持擁有必定數量的空閒內存。linux
Low memorykiller根據兩個原則,進程的重要性和釋放這個進程可獲取的空閒內存數量,來決定釋放的進程。android
進程的重要性,由task_struct->signal_struct->oom_adj決定,程序員
Android將程序的重要性分紅如下幾類,按照重要性依次下降的順序,每一個程序都會有一個oom_adj值,這個值越小,程序越重要,被殺的可能性越低:算法
除了上述程序重要性分類以外,Android系統還維護着另一張表minfree用於維護內存警惕值,這兩張表構成一個對應關係,兩張表在」 /sys/module/lowmemorykiller/parameters/」下保存。shell
adj文件內容以下形如」0,1,2,4,9,15」,minfree文件內容形如」8192,10240,12288,14336,1638,20480」,這樣造成的表結構以下:緩存
例如,當系統可用內存小於12384*4K大小時,會開始殺掉oom_adj>=9級別的進程。app
進程的內存,經過get_mm_rss獲取,在相同的oom_adj下,內存大的,優先被殺。eclipse
源碼位置:kernel/mm/ashmem.c
爲進程間提供提供大塊共享內存,同時爲內核提供回收和管理這個內存的機制。
相比於malloc和anonymous/namedmmap等傳統的內存分配機制,其優點是經過內核驅動提供了輔助內核的內存回收算法機制 (pin/unpin)。什麼是pin和unpin呢?具體來說,就是當你使用Ashmem分配了一塊內存,可是其中某些部分卻不會被使用時,那麼就能夠 將這塊內存unpin掉。
unpin後,內核能夠將它對應的物理頁面回收,以做他用。你也不用擔憂進程沒法對unpin掉的內存進行再次訪問,由於回收後的內存還能夠再次被得到(經過缺頁handler),由於unpin操做並不會改變已經 mmap的地址空間。
源碼位置:drivers/misc/pmem.c
AndroidPmem是爲了實現共享大尺寸連續物理內存而開發的一種機制,該機制對dsp,gpu等部件很是有用。Pmem至關於把系統內存劃分出一部分單獨管理,即不被linux mm管理,實際上linux mm根本看不到這段內存。
Pmem和Ashmem都經過mmap來實現共享內存,其區別在於Pmem的共享區域是一段連續的物理內存,而Ashmem的共享區域在虛擬空間是 連續的,物理內存卻不必定連續。dsp和某些設備只能工做在連續的物理內存上,這樣cpu與dsp之間的通訊就須要經過Pmem來實現。
ION是google在Android4.0ICS爲了解決內存碎片管理而引入的通用內存管理器,它會更加融合kernel。目前QCOM MSM, NVDIA Tegra, TI OMAP, MRVL PXA都用ION替換PMEM。
ION 定義了四種不一樣的heap,實現不一樣的內存分配策略。
Native進程是採用C/C++實現,不包含dalvik實例的進程,/system/bin/目錄下面的程序文件運行後都是以native進程形式存在的,如/system/bin/surfaceflinger、/system/bin/rild、procrank等就是native進程,地址空間結構以下:
java進程是Android中運行於dalvik虛擬機之上的進程。dalvik虛擬機的宿主進程由fork()系統調用建立,因此每個java進程 都是存在於一個native進程中,所以,java進程的內存分配比native進程複雜,由於進程中存在一個虛擬機實例。Android系統中的應用程 序基本都是java進程,如桌面、電話、聯繫人、狀態欄等等,進程結構以下:
Stack空間(進棧和出棧)由操做系統控制,其中主要存儲函數地址、函數參數、局部變量等等,因此Stack空間不須要很大,通常爲幾MB大小。
Heap空間的使用由程序員控制,程序員可使用malloc、new、free、delete等函數調用來操做這片地址空間。Heap爲程序完成 各類複雜任務提供內存空間,因此空間比較大,通常爲幾百MB到幾GB。正是由於Heap空間由程序員管理,因此容易出現使用不當致使嚴重問題。
進程空間中的heap空間是咱們須要重點關注的。heap空間徹底由程序員控制,咱們使用的malloc、C++ new和java new所申請的空間都是heap空間, C/C++申請的內存空間在native heap中,而java申請的內存空間則在dalvik heap中。
進程的內存空間只是虛擬內存(或者叫做邏輯內存),而程序的運行須要的是實實在在的內存,即物理內存(RAM)。在必要時,操做系統會將程序運行中申請的內存(虛擬內存)映射到RAM,讓進程可以使用物理內存。
RAM做爲進程運行不可或缺的資源,對系統性能和穩定性有着決定性影響。另外,RAM的一部分被操做系統留做他用,好比顯存等等,內存映射和顯存等都是由操做系統控制,咱們也沒必要過多地關注它,進程所操做的空間都是虛擬地址空間,沒法直接操做RAM。示意圖以下:
Android系統對dalvik的vm heapsize做了硬性限制,當java進程申請的java空間超過閾值時,就會拋出OOM異常,在/system/build.prop中有三項能夠配置相關信息。
dalvik.vm.heapstartsize:堆分配的初始大小,調整這個值會影響到應用的流暢性和總體ram消耗。這個值越小,系統ram消 耗越慢,可是因爲初始值較小,一些較大的應用須要擴張這個堆,從而引起gc和堆調整的策略,會應用反應更慢。相反,這個值越大系統ram消耗越快,可是程序更流暢。
dalvik.vm.heapgrowthlimit:受控狀況下的極限堆(僅僅針對dalvik堆,不包括native堆)大小,dvm heap是可增加的,可是正常狀況下dvm heap的大小是不會超過dalvik.vm.heapgrowthlimit的值(非正常狀況下面會詳細說明)。這個值控制那些受控應用的極限堆大小, 若是受控的應用dvm heap size超過該值,則將引起oom(out of memory)。
dalvik.vm.heapsize:不受控狀況下的極限堆大小,這個就是堆的最大值。無論它是否是受控的。這個值會影響非受控應用的dalvikheap size。一旦dalvik heap size超過這個值,直接引起oom。
JVM的垃圾原理是這樣的,它把對象分爲年輕代(Young)、年老代(Tenured)、持久代(Perm),對不一樣生命週期的對象使用不一樣的垃圾回收算法。
年輕代(Young):年輕代分爲三個區,一個eden區,兩個Survivor區。程序中生成的大部分新的對象都在Eden區中,當Eden區滿 時,還存活的對象將被複制到其中一個Survivor區,當此Survivor區的對象佔用空間滿了時,此區存活的對象又被複制到另一個 Survivor區,當這個Survivor區也滿了的時候,從第一個Survivor區複製過來的而且此時還存活的對象,將被複制到年老代。
年老代(Tenured):年老代存放的是上面年輕代複製過來的對象,也就是在年輕代中還存活的對象,而且區滿了複製過來的。通常來講,年老代中的對象生命週期都比較長。
持久代(Perm):用於存放靜態的類和方法,持久代對垃圾回收沒有顯著的影響。
Ddms能夠在eclipse中安裝對應的插件,同時在androidsdk的tools文件夾也有一樣的工具,而且功能比eclipse插件更完備。
切換到eclipse的ddms視圖後,從設備中選擇要監控的進程(手機須要開啓usb調試,被監控應用的manifest中android:debuggable應該爲true)
以下圖操做,咱們主要關注dataobject類型的total size數據在使用過程當中是否出現異常的數據波動。
Mat是eclipse的一個插件,安裝完成以後,在ddms試圖下,同時在左側設備試圖上方選擇「Update Heap」和「Dump HPROF file」按鈕,在彈出的窗口中選擇「Leak Suspects Report」並finish,便可出現下面的結果試圖:
進入leaksuspects以後,就能列出全部可能有泄漏的地方以及代碼片斷
一樣該功能用到ddms ,只是裏面提供的一個子功能而已,可以知道全部對象的分配是在代碼的哪一個類,哪一個文件的哪一行。
非靜態內部類的靜態實例容易形成內存泄漏
activity使用靜態成員
使用handler時的內存問題
註冊某個對象後未反註冊
集合中對象沒清理形成的內存泄露
資源對象沒關閉形成的內存泄露
一些不良代碼成內存壓力:Bitmap使用不當;構造Adapter時,沒有使用緩存的 convertView;在常常調用的方法中建立對象(例如循環)
參考資料: