標籤:run 搜尋 作用 存在 margin net com 相關 方法
寫在最前:
本文的思路主要借鑒了2014年AnDevCon開發人員大會的一個演講PPT,加上把網上搜集的各種記憶體零散知識點進行匯總、挑選、簡化後整理而成。
所以我將本文定義為一個工具類的文章,如果你在Android開發中遇到關於記憶體問題,或者馬上要參加面試,或者就是單純的學習或複習一下記憶體相關知識,都歡迎閱讀。(本文最後我會盡量列出所參考的文章)。
記憶體簡介:
RAM(random access memory)隨機存取儲存空間。說白了就是記憶體。
一般Java在記憶體配置時會涉及到以下地區:
寄存器(Registers):速度最快的儲存場所,因為寄存器位於處理器內部,我們在程式中無法控制
棧(Stack):存放基本類型的資料和對象的引用,但對象本身不存放在棧中,而是存放在堆中
堆(Heap):堆記憶體用來存放由new建立的對象和數組。在堆中分配的記憶體,由Java虛擬機器的自動記憶體回收行程(GC)來管理。
靜態域(static field): 靜態儲存地區就是指在固定的位置存放應用程式運行時一直存在的資料,Java在記憶體中專門劃分了一個靜態儲存地區來管理一些特殊的資料變數如靜態資料變數
常量池(constant pool):虛擬機器必須為每個被裝載的類型維護一個常量池。常量池就是該類型所用到常量的一個有序集和,包括直接常量(string,integer和floating point常量)和對其他類型,欄位和方法的符號引用。
非RAM儲存:硬碟等永久儲存空間
堆棧特點對比:
由於篇幅原因,下面只簡單的介紹一下堆棧的一些特性。
棧:當定義一個變數時,Java就在棧中為這個變數分配記憶體空間,當該變數退出該範圍後,Java會自動釋放掉為該變數所分配的記憶體空間,該記憶體空間可以立即被另作他用。
堆:當堆中的new產生數組和對象超出其範圍後,它們不會被釋放,只有在沒有引用變數指向它們的時候才變成垃圾,不能再被使用。即使這樣,所佔記憶體也不會立即釋放,而是等待被記憶體回收行程收走。這也是Java比較占記憶體的原因。
棧:存取速度比堆要快,僅次於寄存器。但缺點是,存在棧中的資料大小與生存期必須是確定的,缺乏靈活性。
堆:堆是一個運行時資料區,可以動態地分配記憶體大小,因此存取速度較慢。也正因為這個特點,堆的生存期不必事先告訴編譯器,而且Java的垃圾收集器會自動收走這些不再使用的資料。
棧:棧中的資料可以共用, 它是由編譯器完成的,有利於節省空間的。
例如:需要定義兩個變數int a = 3;int b = 3;
編譯器先處理int a = 3;首先它會在棧中建立一個變數為a的引用,然後尋找棧中是否有3這個值,如果沒找到,就將3存放進來,然後將a指向3。接著處理int b = 3;在建立完b的引用變數後,因為在棧中已經有3這個值,便將b直接指向3。這樣,就出現了a與b同時均指向3的情況。這時,如果再讓a=4;那麼編譯器會重新搜尋棧中是否有4值,如果沒有,則將4存放進來,並讓a指向4;如果已經有了,則直接將a指向這個地址。因此a值的改變不會影響到b的值。
堆:例如上面棧中a的修改並不會影響到b, 而在堆中一個對象引用變數修改了這個對象的內部狀態,會影響到另一個對象引用變數。
記憶體耗用名詞解析:
VSS - Virtual Set Size 虛擬耗用記憶體(包含共用庫佔用的記憶體)
RSS - Resident Set Size 實際使用實體記憶體(包含共用庫佔用的記憶體)
PSS - Proportional Set Size 實際使用的實體記憶體(比例分配共用庫佔用的記憶體)
USS - Unique Set Size 進程獨自佔用的實體記憶體(不包含共用庫佔用的記憶體)
一般來說記憶體佔用大小有如下規律:VSS >= RSS >= PSS >= USS
OOM:
記憶體泄露可以引發很多的問題:
1.程式卡頓,響應速度慢(記憶體佔用高時JVM虛擬機器會頻繁觸發GC)
2.莫名消失(當你的程式所佔記憶體越大,它在背景時候就越可能被幹掉。反之記憶體佔用越小,在後台存在的時間就越長)
3.直接崩潰(OutOfMemoryError)
ANDROID記憶體面臨的問題:
1.有限的堆記憶體,原始只有16M
2.記憶體大小消耗等根據裝置,作業系統等級,螢幕尺寸的不同而不同
3.程式不能直接控制
4.支援後台多任務處理(multitasking)
5.運行在虛擬機器之上
5R:
本文主要通過如下的5R方法來對ANDROID記憶體進行最佳化:
1.Reckon(計算)
首先需要知道你的app所消耗記憶體的情況,知己知彼才能百戰不殆
2.Reduce(減少)
消耗更少的資源
3.Reuse(重用)
當第一次使用完以後,盡量給其他的使用
5.Recycle(回收)
返回資源
4.Review(檢查)
回顧檢查你的程式,看看設計或代碼有什麼不合理的地方。
Reckon (計算):
瞭解自己應用的記憶體使用量情況是很有必要的。如果當記憶體使用量過高的話就需要對其進行最佳化,因為更少的使用記憶體可以減少ANDROID系統終止我們的進程的幾率,也可以提高多任務執行效率和體驗效果。
下面從系統記憶體(system ram)和堆記憶體(heap)兩個方面介紹一些查看和計算記憶體使用量情況的方法:
System Ram(系統記憶體):
觀察和計算系統記憶體使用量情況,可以使用Android提供給我們的兩個工具procstats,meminfo。他們一個側重於背景記憶體使用量,另一個是運行時的記憶體使用量。
Process Stats: Android 4.4 KitKat 提出了一個新系統服務,叫做procstats。它將協助你更好的理解你app在後台(background)時的記憶體使用量情況。Procstats可以去監視你app在一段時間的行為,包括在後台運行了多久,並在此段時間使用了多少記憶體。從而協助你快速的找到應用中不效率和不規範的地方去避免影響其performs,尤其是在低記憶體的裝置上運行時。你可以通過adb shell命令去使用procstats(adb shell dumpsys procstats --hours 3),或者更方便的方式是運行Process Stats開發人員工具(在4.4版本的手機中點擊Settings > Developer options > Process Stats)
點擊單個條目還可以查看詳細資料
meminfo:
Android還提供了一個工具叫做meminfo。它是根據PSS標準 (Proportional Set Size——實際實體記憶體)計算每個進程的記憶體使用量並且按照重要程度排序。你可以通過命令列去執行它:(adb shell dumpsys meminfo)或者使用在裝置上點擊Settings > Apps > Running(與Procstats不用,它也可以在老版本上運行)
更多關於
Procstats和
meninfo的介紹可以參考我翻譯的一篇文章:Process Stats:瞭解你的APP如何使用記憶體
Heap(堆記憶體):
在程式中可以使用如下的方法去查詢記憶體使用量情況
ActivityManager#getMemoryClass()
查詢可用堆記憶體的限制
3.0(HoneyComb)以上的版本可以通過largeHeap=“true”來申請更多的堆記憶體(不過這算作“作弊”)
ActivityManager#getMemoryInfo(ActivityManager.MemoryInfo)得到的MemoryInfo中可以查看如下Field的屬性:
availMem:表示系統剩餘記憶體lowMemory:它是boolean值,表示系統是否處於低記憶體運行hreshold:它表示當系統剩餘記憶體低於好多時就看成低記憶體運行
android.os.Debug#getMemoryInfo(Debug.MemoryInfo memoryInfo)
得到的MemoryInfo中可以查看如下Field的屬性:
dalvikPrivateDirty: The private dirty pages used by dalvik。dalvikPss :The proportional set size for dalvik.dalvikSharedDirty :The shared dirty pages used by dalvik.nativePrivateDirty :The private dirty pages used by the native heap.nativePss :The proportional set size for the native heap.nativeSharedDirty :The shared dirty pages used by the native heap.otherPrivateDirty :The private dirty pages used by everything else.otherPss :The proportional set size for everything else.otherSharedDirty :The shared dirty pages used by everything else.
dalvik:是指dalvik所使用的記憶體。native:是被native堆使用的記憶體。應該指使用C\C++在堆上分配的記憶體。other:是指除dalvik和native使用的記憶體。但是具體是指什麼呢?至少包括在C\C++分配的非堆記憶體,比如分配在棧上的記憶體。private:是指私人的。非共用的。share:是指共用的記憶體。PSS:實際使用的實體記憶體(比例分配共用庫佔用的記憶體) PrivateDirty:它是指非共用的,又不能換頁出去(can not be paged to disk )的記憶體的大小。比如Linux為了提高分配記憶體速度而緩衝的小對象,即使你的進程結束,該記憶體也不會釋放掉,它只是又重新回到緩衝中而已。SharedDirty:參照PrivateDirty我認為它應該是指共用的,又不能換頁出去(can not be paged to disk )的記憶體的大小。比如Linux為了提高分配記憶體速度而緩衝的小對象,即使所有共用它的進程結束,該記憶體也不會釋放掉,它只是又重新回到緩衝中而已。
android.os.Debug#getNativeHeapSize()
返回的是當前進程navtive堆本身總的記憶體大小
android.os.Debug#getNativeHeapAllocatedSize()
返回的是當前進程navtive堆中已使用的記憶體大小
android.os.Debug#getNativeHeapFreeSize()
返回的是當前進程navtive堆中已經剩餘的記憶體大小
Memory Analysis Tool(MAT):
通常記憶體泄露分析被認為是一件很有難度的工作,一般由團隊中的資深人士進行。不過,今天我們要介紹的 MAT(Eclipse Memory Analyzer)被認為是一個“傻瓜式“的堆轉儲檔案分析工具,你只需要輕輕點擊一下滑鼠就可以產生一個專業的分析報告。
如:
關於詳細的MAT使用我推薦下面這篇文章:使用 Eclipse Memory Analyzer 進行堆轉儲檔案分析
寫在最後:
我準備將文章分為上、中、下三部分。現在已經全部完成:
記憶體簡介,Recoken(計算)請看:ANDROID記憶體最佳化(大匯總——上)
Reduce(減少),Reuse(重用) 請看:ANDROID記憶體最佳化(大匯總——中)
Recycle(回收), Review(檢查) 請看:ANDROID記憶體最佳化(大匯總——全)
寫這篇文章的目的就是想弄一個大匯總,將零散的記憶體知識點總結一下,如果有錯誤、不足或建議都希望告訴我。
參考文章:
AnDevCon開發人員大會演講PPT:Putting Your App on a Memory Diet
深入Java核心 Java記憶體配置原理精講(http://developer.51cto.com/art/201009/225071.htm)
Process Stats: Understanding How Your App Uses RAM(http://blog.csdn.net/a396901990/article/details/38390135)Android中如何查看記憶體(http://blog.csdn.net/hudashi/article/details/7050897)
Android記憶體效能最佳化(內部資料總結)(http://www.2cto.com/kf/201405/303276.html)
ANDROID記憶體最佳化(大匯總——上)