一、Android內存管理機制
1、Java對象的生命週期
Java對象的生命週期經歷7個階段,分別是創建階段、應用階段、不可見階段、不可達階段、收集階段、終結階段、對象控件重新分配階段。
2、內存回收機制
內存的三個區域
內存會有三個區域,Yong Generation(年輕代)、Old Generation(年老代)、permanent Generation(持久代)。
其中年輕代裏面又分爲三個區,eden、S0、S1。
內存的處理過程:
1.對象創建後在Eden區域,
2.執行GC時,如果對象仍然存貨,則複製到S0區。
3.當S0區滿時,改區域存活對象將複製到S1區,然後S0清空,接下來S0和S1角色互換。
4.當第三部達到一定次數後,存活對象將被複制到Old Generation。
5.當這個對象在Old Generation區域挺溜的時間達到一定程度時,它會被移動到Old Generation,最後積累一定時間再移動到Permaent Generation區域,Permaent Generation區域也存放一些靜態文件。
GC回收的一些算法
Copying算法:掃描出存活的對象,並複製到一塊新的完全未使用的控件中,對應於Young Generation,就是在Eden、FromSpace或ToSpace之間copy。
標記算法:掃描出存活對象,然後再回收未標記的對象,回收後對空出的空間要麼合併,要麼標記出來便於下次分配,以減少內存碎片帶來的損耗。年老代對象存活時間較長較穩定,使用標記算法回收。
GC類型
1.kGcCauseForAlloc:在分配內存時內存不夠情況下引起的GC,這種情況下GC會stop World。Stop World 是由於併發GC時,其他線程都會停止。
2.kGcCauseBackground:當內存達到一定閾值的時候引發GC,這個時候是一個後臺GC,不會引起Stop World。
3.kGcCauseExplicit:顯示調用時進行的GC,如果ART打開了這個選項,在system.gc時會進行GC。
其他GC注意事項
1.儘量不去顯式調用 system.gc() 減少不必要的系統開銷,影響應用的流暢度。
2.儘量減少內存泄露,避免OOM。
二、Android內存泄露
1、什麼是內存泄露?
java對象有自己的生命週期,當這個對象不需要再使用時,應該完整地走完生命週期,但因爲某些原因,對象雖然已經不再使用,仍然在內存中並沒有結束整個生命週期,這就意味着這個對象已經泄露了。
GC會選擇一些還存活的對象作爲內存遍歷的根節點 GC Roots,通過對GC Roots的可達性來判斷是否需要回收。
Android系統虛擬機的垃圾回收是通過虛擬機GC機制來實現的。GC會選擇一些存活的對象作爲內存便利的跟節點GC Roots,通過判斷GC Roots的可達性來判斷是否需要回收,如上圖其中 1 2 3 4直接或間接被GC Roots引用鏈相連,這類對象被認爲還需要使用的對象,就不會被回收。5 6 7將會被回收。到那時這裏如果Object4 如果不需要使用的話這時候也不會被回收,就屬於內存泄露。
2、常見內存泄露場景以及注意事項
~ 資源型對象未關閉
~ 註冊對象未註銷
~ 類的靜態變量持有大數據對象 如bitmap
~ 費靜態內部類的靜態實例
~ Handler臨時性內存泄露
~ 容器中的對象沒有清理造成的內存泄露
3、內存泄露分析工具
leakcanary
三、常見注意事項避免內存消耗過多
1、AutoBoxing自動裝箱過程
Integer num=0;
for(int i=0;i<100;i++){
num+=i;
}
這段代碼每次循環,虛擬機都必須創建一個新的整數對象,並把它加到其他整數對象前面,創建一個新的整數對象,意味着要消耗更多性能。int只有4字節,而Integer對象有16字節。
2、內容複用
1、有效利用系統自帶資源。
2、視圖複用,如ViewHolder。
3、對象池。
4、Bitmap對象複用。
3、使用最優的數據類型
1、當對象的數目在1000以內且特別多訪問而刪除和插入不高的時候儘量用ArrayMap替代HashMap。
2、枚舉的最大優點是安全、易讀,但是內存消耗是定義常量的三倍以上。可以使用註解方式來檢查安全。
3、使用IntDef和StringDef檢查類型安全。
4、LruCache建議使用這個緩存機制,但是既不能分配太大,也不能分配太小。
4、圖片的內存優化
設置位圖規格,使用inSampleSize實現位圖縮放和壓縮。使用緩存機制等。
四、內存分析工具
1、Memory Monitor
這個是一個我們開發過程中很常用的內存、CPU、網絡的分析工具。
界面很直觀,左上角有運行的機型和項目包名,然後最直觀的動態圖,分別是CPU、Memory、NetWork。點進去可以進入的Memory。
這裏可以清晰的看到顏色對應區域佔用的內存大小。
通過這兩張圖 內存的大部分信息都能查閱到。我們在操作APP加載圖片等操作時候能看到內存上升、和下降,如果操作APP後發現內存不會下降可能就是我們有些對象沒有及時釋放,也有可能導致內存泄露。
還有對應的模擬GC,查看時間段具體內存信息以及一段時間的內存追蹤等工具可以使用。可以定位到具體的代碼行。如下圖:
這裏有一個Shallow size這個屬性的概念:
Shallow size就是對象本身佔用內存的大小,不包含其引用的對象。常規對象(非數組)的Shallow size有其成員變量的數量和類型決定。數組的shallow size有數組元素的類型(對象類型、基本類型)和數組長度決定。
2、Heap Viewrer
如果是Android Studio的話通過Tools->Android->Android Device Monitor找到這個工具。
進入後選擇運行APP的包名然後點擊update Heap按鈕,這時候會在每次gc時展示數據信息,也可以在後面手動GC,如果在操作頁面這時候可能會發現小卡頓,因爲在GC時,可能導致其他線程停止工作,這時可以清晰看到表中內存信息:
頭部總覽視圖:
標題 | 含義 |
---|---|
Heap Size | 堆棧分配給APP的內存大小。 |
Allocated | 已分配使用的內存大小。 |
Free | 空閒的內存大小。 |
%Used | Allocated/Heap Size 的使用率。 |
Object | 對象數量 |
下面詳情視圖:
標題 | 含義 |
---|---|
free | 空閒的對象 |
data object | 數據對象,Java類類型對象,是最主要的觀察對象。 |
class object | java類類型的引用對象。 |
1-byte array(byte[],boolean[]) | 一字節的數組對象。 |
2-byte array(short[],char[]) | 兩字節的數組對象。 |
4-byte array(object[],int[],float[]) | 4字節的數組對象。 |
8-byte array(long[],double[]) | 8字節的數組對象。 |
non-java object | 非Java對象。 |
每個類型的數據值對應:
標題 | 含義 |
---|---|
Count | 數量 |
Total Size | 總共佔用的內存的大小 |
Smallest | 將對象佔用內存從小到大排列,排在第一個對象佔用內存大小 |
Largest | 將對象佔用內存從小到大排列,排在最後一個對象佔用的內存大小。 |
Median | 將對象佔用內存從小到大排列,排在總監的對象佔用的內存大小。 |
Average | 平均值 |