轉載請註明本文出自大苞米的博客(http://blog.csdn.net/a396901990),謝謝支持!
寫在最前:
本文的思路主要借鑑了2014年AnDevCon開發者大會的一個演講PPT,加上把網上搜集的各種內存零散知識點進行彙總、挑選、簡化後整理而成。
所以我將本文定義爲一個工具類的文章,如果你在ANDROID開發中遇到關於內存問題,或者馬上要參加面試,或者就是單純的學習或複習一下內存相關知識,都歡迎閱讀。(本文最後我會盡量列出所參考的文章)。
一般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:
Heap(堆內存):
在程序中可以使用如下的方法去查詢內存使用情況
ActivityManager#getMemoryClass()
查詢可用堆內存的限制
3.0(HoneyComb)以上的版本可以通過largeHeap=“true”來申請更多的堆內存(不過這算作“作弊”)
android.os.Debug#getMemoryInfo(Debug.MemoryInfo memoryInfo)
得到的MemoryInfo中可以查看如下Field的屬性:
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)