Android的內存分配、管理、OOM這一篇文章就夠夠的了!

Android的內存分配和管理爲兩個大板塊: (Android手機系統和手機應用APP)

一、Android 手機系統的內存分配和管理

主要3點內容介紹
-Android 系統的低內存工作機制:
-Android 應用內存的使用情況:
-如何減少Android 應用內存的佔用:

1、Android 系統的低內存工作機制:

首先給大家提出一個問題,爲什麼有的手機打開應用多了,在使用的時候回特別卡呢。並且有時候回卡在APP裏面好長時間呢?甚至黑屏,重啓這些現象呢?那麼今天我就給大家一一揭曉謎底:

先給大家介紹一個物理內存概念: 然後引入Android Low Memory Killer。(低內存殺手)

手機內存(物理內存): 設備的物理內存被分爲很多頁(Page),每頁4KB, 不同的頁用來做不同的事情:
-橘黃色代表已使用的頁
-黃色代表緩存頁(數據在磁盤上有備份,所以Cache Pages是可以被回收的)
-綠色是空閒頁

在這裏插入圖片描述

用於回收Cached Pages(緩存頁內存)的kswapd進程
Cached Pages緩存頁什麼時候被回收呢? 下面我們分析一下

下面是一個2GB內存的手機,X軸代表使用時間, Y軸代表內存情況。隨着打開的應用越來越多,Userd Pages(使用內存)也越來越多,而Cached Pages(緩存頁)和Free Pages(空閒頁)越來越少。當Free Pages(空閒頁)低於kswapd(回收進程)的閥值時,linux內核就會通過kswapd進程Cached Pages(緩存頁)就行回收。當應用再次訪問Cached Pages(緩存頁)的內容時,就需要從磁盤上重新加載。如果Cached Pages(緩存頁)太少,設備可能就死機。
在這裏插入圖片描述
所以Androd 上有個機制叫做 Low Memory Killer,當 Cached Pages 太少時,就會被觸發。它的工作方式是根據進程的優先級,選擇性地殺死某個進程,釋放該進程佔用的所有資源以滿足內存分配需要:
在這裏插入圖片描述
如上圖所示,當 Cached Pages 低於 LMK 閾值時,將會觸發低內存殺死機制。

LMK(Low Memory Killer)
如果 LMK 殺掉的是用戶正在交互或可以感知的進程,將會導致非常不友好的用戶體驗。所以 Android SystemServer 進程維護了一張進程優先級列表,LMK 根據這張表來決定先殺死哪個進程:
在這裏插入圖片描述
-Perceptible 指的是非用戶直接交互的進程,比如在後臺播放音樂的音樂播放器進程;
-Previous 指的是切換至當前前臺應用前的應用進程;
-Cached 指緩存的進程,這可能是退至後臺的應用進程,也可能是已經退出的應用進程,目的是爲了實現應用間的快速切換。所以,Cached 進程也是優先級最低的進程:

在這裏插入圖片描述
如上圖所示,當已用內存超過 LMK 閾值時,LMK 將從 Cached 列表底部開始殺死進程。如果可用內存還是不滿足分配需要,那麼將會按照上表所示優先級自底向上殺死進程,直到準備 Kill SystemServer 進程,這將導致手機重啓。

所以,你可以想象 LMK 在低內存手機上的情景:如圖所示
在這裏插入圖片描述

以上就很好的解釋了我文章開始提問那個問題,有的手機爲什麼會卡頓等等。原因就是當手機內存小,同時使用的使用時間過長,已使用的內存越來越多,緩存頁和空閒頁越來越少,LMK 將一直處於活躍狀態,具體表現就是應用卡頓、桌面黑屏重啓,手機死機等等。

所以在開發中,有的同事拿來特別low的手機,給你說,你看我們APP特別卡,怎麼回事?要求你解決這個問題等等, 你就可以大聲懟他了!哈哈哈…

2、Android 應用內存的使用情況:

那麼,我們怎麼知道 App 使用了多少內存呢?

物理內存追蹤

之前提到,設備的物理內存被分爲很多頁(Page),Linux Kernel 將會持續跟蹤每個進程使用的 Pages,所以只要對進程使用的 Pages 進行計數即可:
在這裏插入圖片描述
但實際情況遠比這要複雜的多,因爲有些 Pages 是進程間共享的:
共享內存頁計數方法

1、RSS(Resident Set Size):App 完全負責
在這裏插入圖片描述
2、PSS(Proportional Set Size):App 按比例負責,比如下圖所示兩個進程共享,那就負責一半。如果三個進程共享,那就負責三分之一:
在這裏插入圖片描述
3、USS(Unique Set Size):App 無責
在這裏插入圖片描述
但實際上,至少需要系統級別的上下文才能知道識別 RSS 與 USS。所以通常都是使用 PSS 來計算,這也可以避免多計或者少計 Shared Pages。你可以使用:

adb shell dumpsys meminfo -s [process] 

命令來查看一個進程的 PSS 使用情況:
最底部的 TOTAL 代表的就是應用按比例佔用的總內存大小。
在這裏插入圖片描述

減少應用內存佔用

使用 Android Studio 的 Memory Profiler,可以查看當前 Java 堆上分配了哪些對象、對象大小以及對象引用鏈和被引用鏈等很多信息。Live Allocation 中有 image heap、zygote heap、app heap 等可以選擇,但是我建議你只關注 app heap。因爲 image heap 和 zygote heap 是 App 啓動時從系統繼承過來的,對於這部分內存佔用,我們基本上無能爲力:

在這裏插入圖片描述
內存優化建議:
1、優化Java堆上的對象
2、減小apk體積,因爲很多在apk中佔據磁盤空間的文件,在運行期也會佔據內存的空間。


二、手機應用APP的內存分配和管理

-APP的內存是如何分配的?

當我們安裝一個應用完成以後,手機系統會爲其分配一塊內存空間(根據手機內存的大小分配,是有上限的)。當然我們也可以配置最大的分配內存:如下

<application
     .....
          android:largeHeap="true">   這個屬性可以理解成擴容用的,就是讓手機爲自己開闢內存的時候多給一點,具體給多少每款手機不固定,即時是官方設定了值,還多手機廠商還是對這個值進行了修改。
    .......
</application>
-Java代碼的內存分配

上篇文章我也介紹了虛擬機的內存分配,對內存有有疑問的可以看上篇虛擬機內存。
OOM,垃圾回收我們針對的是堆內存我們通過代碼分析一下, 內存是怎麼分配到堆內存裏面去的。

public class A{
	int a = 1;
	Test mTest = new Test();

	public void method(){
		int a2 = 1;
		Test mTest2 = new Test();
	}
}

A mA = new A();

我上面寫的代碼,基本程序中都會這樣的,我們分析一下
method方法中的a2和對象引用mTest2,都放在棧中(也就是我上篇講到的方法中運行是在棧中),但是mTest實例的對象是放在堆中,
mA對象實例存放在堆中,包括這個對象裏面所有的成員變量a和mTest也是存在堆中,而mA這個引用也是存在棧中。

經過上面代碼的分析,大家在解決內存泄漏的時候會很容易着手了。

得出結論

1.局部變量的基本數據類型和引用存儲於棧中,引用的對象實體存儲於堆中。因爲它們屬於方法中的變量,生命週期隨方法而結束。
2.成員變量全部存儲與堆中(包括基本數據類型,引用和引用的對象實體),因爲它們屬於類,類對象終究是要被new出來使用的。
3.我們所說的內存泄露,只針對堆內存,他們存放的就是引用指向的對象實體。

什麼情況會觸發GC回收

先解釋一下垃圾回收收集器GC機制,它是自動完成的,它是從根節點開始遍歷查詢運行的對象,有沒有被引用,標誌是不是垃圾,如果是垃圾就會回收掉!它針對的只是堆內存。

GC觸發時機:
-GC_CONCURRENT: 當我們應用程序的堆內存快要滿的時候,系統會自動觸發GC操作來釋放內存(到達分配內存的閾值)。
-GC_FOR_MALLOC: 當我們的應用程序需要分配更多內存,可是現有內存已經不足的時候,系統會進行GC操作來釋放內存。
-GC_HPROF_DUMP_HEAP: 當生成HPROF文件的時候,系統會進行GC操作,關於HPROF文件我們下面會講到。
-GC_EXPLICIT: 這種情況就是我們剛纔提到過的,主動通知系統去進行GC操作,比如調用System.gc()方法來通知系統。或者在DDMS中,通過工具按鈕也是可以顯式地告訴系統進行GC操作的。

爲什麼會產OOM?

假如手機爲我們應用開闢了100M內存, 而這塊內存規定閾值規定的是到達使用70M,觸發垃圾回收。 如果應用到達70M,那麼會回收垃圾,那麼如果代碼造成內存泄漏,也就是垃圾對象的實例一直被引用,那麼就有可以清理不乾淨,本應該回收50M,使用可到達20M。但是由於堆空間沒有回收乾淨,只回收了30M,那麼我們就使用就會使40M。 經過對比發現會有20M是永遠不能回回收的。這時候我們代碼越寫越多,等到多次GC機制執行以後,造成的垃圾內存大於100M,那麼就會出現OOM,從而使應用崩潰。

-如何定位內存泄漏

那麼我們如何定位呢,我一般會有用兩個辦法,
方法1: LeakCanary,可以定位到代碼泄漏的地方。

方法2:studio定位:(只解決head裏面的內存即可,這個工具也可以定位到代碼)
在這裏插入圖片描述

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