ANDROID內存優化(大彙總——上)

轉載請註明本文出自大苞米的博客(http://blog.csdn.net/a396901990),謝謝支持!


寫在最前:

本文的思路主要借鑑了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提供給我們的兩個工具procstatsmeminfo。他們一個側重於後臺的內存使用,另一個是運行時的內存使用。

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不用,它也可以在老版本上運行)

更多關於Procstatsmeninfo的介紹可以參考我翻譯的一篇文章: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)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章