Android 內(nèi)存管理

  1. 概述
  2. 虛擬內(nèi)存
    2.1 分頁
    2.2 內(nèi)存映射
  3. 內(nèi)存不足時的處理
    3.1 kswapd
    3.2 LMK
  4. 虛擬機(jī)
    4.1 堆空間劃分
    4.2 回收算法

在看這篇文章之前,需要Linux內(nèi)存管理基礎(chǔ),推薦Linux 內(nèi)存管理

對于這篇文章的結(jié)構(gòu)我也是思慮再三,為什么先講LMK,后講虛擬機(jī)回收,主要是因為,分頁、內(nèi)存映射、LMK是直接影響物理內(nèi)存的,而虛擬機(jī)對應(yīng)的更多的是硬件無關(guān)的。此外,本篇不涉及具體代碼實現(xiàn),詳細(xì)實現(xiàn)以后再補(bǔ)。

概述

Android 使用的是Linux內(nèi)核,但是這個Linux內(nèi)核是根據(jù)Android所需,在文件系統(tǒng)、內(nèi)存管理、進(jìn)程間通信機(jī)制和電源管理方面等方面進(jìn)行了修改了的,繼承了Linux內(nèi)核的諸多優(yōu)點,保留了Linux內(nèi)核的主題框架,同時能夠更好的工作在移動設(shè)備上。

Android同樣使用分頁內(nèi)存映射來構(gòu)建虛擬內(nèi)存,同時使用垃圾回收器回收內(nèi)存,使用LowMemoryKiller(LMK)在低內(nèi)存的時候來殺死進(jìn)程釋放更多內(nèi)存。

值得注意的是,應(yīng)用修改的任何內(nèi)存,無論修改的方式是分配新對象還是輕觸內(nèi)存映射的頁面,都會一直駐留在 RAM 中,并且不會換出到磁盤。

每一個應(yīng)用都是一個獨立的虛擬機(jī),以一個Linux進(jìn)程的形式存在,在Android上,4.4之前都是Dalvik虛擬機(jī),5.0之后默認(rèn)都是ART虛擬機(jī)了,對于垃圾回收機(jī)制,Android ART虛擬機(jī)默認(rèn)使用CMS來清理回收對象。

對于回收進(jìn)程資源這回事,Linux在進(jìn)程退出的時候,就會釋放當(dāng)前Linux的內(nèi)存,但是Android為了提高的切換進(jìn)程時的啟動速度,會將這些進(jìn)程都保存在內(nèi)存中,知道系統(tǒng)需要更多的內(nèi)存為止。LMK每個一段時間都會檢查一次,當(dāng)內(nèi)存值較低的時候,LMK就會根據(jù)不同的剩余內(nèi)存檔位來來選擇殺不同優(yōu)先級的進(jìn)程。其實在Linux也有類似的管理策略,即OOM killer,全稱(Out Of Memory Killer), OOM的策略更多的是用于分配內(nèi)存不足時觸發(fā),得分最高的進(jìn)程殺掉。當(dāng)需要OOM killer處理的時候,系統(tǒng)可能已經(jīng)處于異常狀態(tài),而Android更像是在未雨綢繆。

虛擬內(nèi)存

分頁

分頁的Linux很多具體實現(xiàn)我們已經(jīng)聊過,既然Android是從Linux開始改的,那么很多其實都是一樣的,這里著重介紹一下他不同的地方。

首先,Android的內(nèi)存形式被分為三種:RAM、zRAM 和存儲器。

  • RAM 就是我們常說的物理內(nèi)存,是最快的內(nèi)存類型,但其大小通常有限。
  • zRAM 是從RAM開辟出來的一塊區(qū)域,是用于交換空間的 RAM 分區(qū)。所有數(shù)據(jù)在放入 zRAM 時都會進(jìn)行壓縮,然后在從 zRAM 向外復(fù)制時進(jìn)行解壓縮。這部分 RAM 會隨著頁面進(jìn)出 zRAM 而增大或縮小。設(shè)備制造商可以設(shè)置 zRAM 大小上限。
  • 存儲器,這一部分就是我們常說的磁盤,存儲器中包含所有持久性數(shù)據(jù)(例如文件系統(tǒng)等),以及為所有應(yīng)用、庫和平臺添加的對象代碼,存儲器比另外兩種內(nèi)存的容量大得多。

在 Android 上,存儲器不像在其他 Linux 實現(xiàn)上那樣用于交換空間,因為頻繁寫入會導(dǎo)致這種內(nèi)存出現(xiàn)損壞,并縮短存儲媒介的使用壽命,而是所有內(nèi)存都是一直駐留RAM中。只是在實現(xiàn)分頁的時候,需要一塊輔村來做擔(dān)保,Linux選擇了磁盤,Android選擇了壓縮+RAM。這部分跟Linux大同小異,所以就不展開敘述,Linux參考這里.

對于RAM與zRAM,Android 使用了跟 Linux 相似的管理手法,分頁。RAM 分為多個“頁”。通常,每個頁面為 4KB 的內(nèi)存。系統(tǒng)會將頁面分為“可用”或“已使用”。對于已使用的內(nèi)存可以分為以下類別:

  • 緩存頁:在儲存器中有對應(yīng)文件的內(nèi)存,例如,代碼或內(nèi)存映射文件。緩存頁也分為兩種:
    • 干凈頁(clean):存儲器中未經(jīng)修改的文件副本,可由 kswapd刪除以增加可用內(nèi)存
    • 臟頁(dirty):存儲器中已經(jīng)被修改的文件副本,可由 kswapd 移動到 zRAM 中進(jìn)行壓縮儲存
  • 共享頁:由多個進(jìn)程共享使用的頁面
    • 干凈頁:存儲器中未經(jīng)修改的文件副本,可由 kswapd 刪除以增加可用內(nèi)存
    • 臟頁:存儲器中已經(jīng)被修改的文件副本;允許通過 kswapd 或者通過明確使用 msync() 或 munmap() 將更改寫回存儲器中的文件,然后就變成干凈頁,可刪除
  • 匿名頁:存儲器中沒有對應(yīng)文件的內(nèi)存(例如,由設(shè)置了 MAP_ANONYMOUS 標(biāo)記的 mmap() 進(jìn)行分配)
    • 臟頁:可由 kswapd 移動到 zRAM/在 zRAM 中進(jìn)行壓縮以增加可用內(nèi)存

匿名頁中,由于不可以回寫到存儲器,無論是在RAM或者zRAM中,一直都在內(nèi)存中,所以只能是臟頁。同時,干凈頁可以刪除,因為始終可以使用存儲器中的數(shù)據(jù)重新生成它們。

內(nèi)存映射

內(nèi)存映射(mmap)是一種內(nèi)存映射文件的方法,即將一個文件或者其他對象映射到進(jìn)程的地址空間,實現(xiàn)文件磁盤地址和應(yīng)用程序進(jìn)程虛擬地址空間中一段虛擬地址的一一映射關(guān)系。實現(xiàn)這樣的映射關(guān)系后,進(jìn)程就可以采用指針的方式讀寫操作這一段內(nèi)存,而系統(tǒng)會自動回寫臟頁面到對應(yīng)的文件磁盤上。應(yīng)用程序處理映射部分如同訪問主存。

分頁和內(nèi)存映射的區(qū)別:

在于分頁文件操作在進(jìn)程訪存時是需要先查詢頁面緩存(page cache)的,若發(fā)生缺頁中斷,需要通過inode定位文件磁盤地址,先把缺失文件復(fù)制到page cache,再從page cache復(fù)制到內(nèi)存中,才能進(jìn)行訪問。這樣訪存需要經(jīng)過兩次文件復(fù)制,寫操作也是一樣。總結(jié)來說,常規(guī)文件操作為了提高讀寫效率和保護(hù)磁盤,使用了頁緩存機(jī)制。這樣造成讀文件時需要先將文件頁從磁盤拷貝到頁緩存中,由于頁緩存處在內(nèi)核空間,不能被用戶進(jìn)程直接尋址,所以還需要將頁緩存中數(shù)據(jù)頁再次拷貝到內(nèi)存對應(yīng)的用戶空間中。但mmap的優(yōu)勢在于,把磁盤文件與進(jìn)程虛擬地址做了映射,這樣可以跳過page cache,只使用一次數(shù)據(jù)拷貝

內(nèi)存不足時的處理

Android 有兩種處理內(nèi)存不足的進(jìn)程:內(nèi)核交換守護(hù)進(jìn)程(kswapd)和低內(nèi)存終止守護(hù)進(jìn)程(LowMemoryKiller)。對應(yīng)著兩種內(nèi)存不足時候的處理方式。

kswapd

kswapd, 是 Linux 內(nèi)核的一部分,用于將已使用內(nèi)存轉(zhuǎn)換為可用內(nèi)存。當(dāng)設(shè)備上的可用內(nèi)存不足時,該守護(hù)進(jìn)程將變?yōu)榛顒訝顟B(tài)。Linux 內(nèi)核設(shè)有可用內(nèi)存上下限閾值。當(dāng)可用內(nèi)存降至下限閾值以下時,kswapd 開始回收內(nèi)存頁。當(dāng)可用內(nèi)存達(dá)到上限閾值時,kswapd 停止回收內(nèi)存頁。

kswapd 可以對干凈頁進(jìn)行刪除,當(dāng)進(jìn)程需要該頁面的時候,只需要從存儲器中讀回。同時也可以將臟頁移入zRAM中壓縮存儲,壓縮率越高,相對釋放的內(nèi)存也就越多。如果進(jìn)程需要該頁,解壓調(diào)出即可,跟Linux中的交換分區(qū)處理邏輯相同。

LMK

系統(tǒng)會定時檢查內(nèi)存可用值,當(dāng)?shù)陀谔囟ǖ拈撝担琇MK就會開始?xì)⑦M(jìn)程,直到可用內(nèi)存值高于閾值才會停止殺內(nèi)存。在介紹LMK之前,我們需要了解LMK的三個關(guān)鍵參數(shù):

  • oom_adj:在 Framework 層使用,代表進(jìn)程的優(yōu)先級,數(shù)值越高,優(yōu)先級越低,越容易被殺死。
  • oom_adj threshold:在 Framework 層使用,代表 oom_adj 的內(nèi)存閾值。Android Kernel 會定時檢測當(dāng)前剩余內(nèi)存是否低于這個閥值,若低于這個閾值,則會根據(jù) oom_score_adj 參數(shù)的值殺進(jìn)程,數(shù)值越大越先殺。
  • oom_score_adj: 在 Kernel 層使用,由 oom_adj 換算而來,是殺死進(jìn)程時實際使用的參數(shù)。

在Android 6.0(API23)及之前版本 oom_adj 的取值范圍為 [-17, 16],在這里給出 oom_adj 和 oom_score_adj 轉(zhuǎn)換關(guān)系:

  • 當(dāng)oom_adj = 15, 則oom_score_adj=1000;
  • 當(dāng)oom_adj < 15, 則oom_score_adj= oom_adj * 1000/17;

在簡述LMK流程之前,需要對于這三個參數(shù)有一定的認(rèn)識哦。下面就是 adj 值的列表,值越大越容易被殺:

常量定義 常量取值 含義
NATIVE_ADJ -1000 native進(jìn)程(不被系統(tǒng)管理)
SYSTEM_ADJ -900 系統(tǒng)進(jìn)程
PERSISTENT_PROC_ADJ -800 系統(tǒng)persistent進(jìn)程,比如telephony
PERSISTENT_SERVICE_ADJ -700 關(guān)聯(lián)著系統(tǒng)或persistent進(jìn)程
FOREGROUND_APP_ADJ 0 前臺進(jìn)程
VISIBLE_APP_ADJ 100 可見進(jìn)程
PERCEPTIBLE_APP_ADJ 200 可感知進(jìn)程,比如后臺音樂播放
BACKUP_APP_ADJ 300 備份進(jìn)程
HEAVY_WEIGHT_APP_ADJ 400 后臺的重量級進(jìn)程,system/rootdir/init.rc文件中設(shè)置
SERVICE_ADJ 500 服務(wù)進(jìn)程
HOME_APP_ADJ 600 Home進(jìn)程
PREVIOUS_APP_ADJ 700 上一個App的進(jìn)程
SERVICE_B_ADJ 800 B List中的Service(較老的、使用可能性更小)
CACHED_APP_MIN_ADJ 900 不可見進(jìn)程的adj最小值
CACHED_APP_MAX_ADJ 906 不可見進(jìn)程的adj最大值
UNKNOWN_ADJ 1001 一般指將要會緩存進(jìn)程,無法獲取確定值

對此,官網(wǎng)也給出了一個殺進(jìn)程的順序圖:


lmkd 是由 init進(jìn)程通過解析 init.rc 文件來啟動的守護(hù)進(jìn)程,可監(jiān)控運行中的 Android 系統(tǒng)的內(nèi)存狀態(tài),并通過終止最不必要的進(jìn)程來解決內(nèi)存壓力,使系統(tǒng)以可接受的水平運行。lmkd 需要跟 framework 層交互,因為framework層知道各個進(jìn)程當(dāng)前的狀態(tài)(比如是否在前臺等),lmkd 進(jìn)程會創(chuàng)建名為 lmkd 的 socket,節(jié)點位于 /dev/socket/lmkd,用于與framework交互。

lmkd 啟動后,便會進(jìn)入循環(huán)等待狀態(tài),接受來自 ProcessList 的三個命令:

命令 功能 方法
LMK_TARGET 初始化 oom_adj ProcessList::setOomAdj()
LMK_PROCPRIO 更新 oom_adj ProcessList::updateOomLevels()
LMK_PROCREMOVE 移除進(jìn)程(暫時無用) ProcessList::remove()

Android 四大組件直接影響了oom_adj值,ActivityManagerService 會根據(jù)當(dāng)前應(yīng)用進(jìn)程托管組件(即四大組件)生命周期的變化,及時的調(diào)用 applyOomAdjLocked(),更新進(jìn)程狀態(tài)及該狀態(tài)對應(yīng)的 oom_adj。

虛擬機(jī)

為什么需要 Java 虛擬機(jī)?為什么 Java 說 write once, run everywhere ?

在各個平臺都有對應(yīng)的虛擬機(jī),他們負(fù)責(zé)將符合JVM對加載編譯文件格式要求的語言進(jìn)行解釋執(zhí)行。JVM屏蔽了與具體操作系統(tǒng)平臺相關(guān)的信息,使得Java程序只需生成在Java虛擬機(jī)上運行的目標(biāo)代碼字節(jié)碼,就可以在多種平臺上不加修改地運行。這也意味,JVM 有自己完善的硬體架構(gòu),如處理器堆棧寄存器等,還具有相應(yīng)的指令系統(tǒng),與具體的操作系統(tǒng)無關(guān)。

而在 Android 上,同JVM一樣,Dalvik 或者 ART 負(fù)責(zé)將字節(jié)碼解釋成 Android 可以執(zhí)行的指令,而每一個Dalvik 或者 ART 提供了對象生命周期管理、堆棧管理、線程管理、安全和異常管理以及垃圾回收等重要功能,各自擁有一套完整的指令系統(tǒng)。但是,JVM其核心目的,是為了構(gòu)建一個真正跨OS平臺,跨指令集的程序運行環(huán)境(VM),DVM的目的更像是為了將android OS的本地資源和環(huán)境,以一種統(tǒng)一的界面提供給應(yīng)用程序開發(fā)

堆空間劃分

網(wǎng)上大部分都是堆Dalvik和ART主要講解的都是堆內(nèi)存及回收,想來其他區(qū)域的原理與Java虛擬機(jī)規(guī)范原理差不多。話不多說,我們先看 Dalvik 和 ART 虛擬機(jī)對運行時堆的空間劃分:

Dalvik 堆

在 Dalvik 中,默認(rèn)使用 標(biāo)記-清除(Mark-Sweep)算法 來進(jìn)行垃圾回收,在運行時,堆被分成2個Space和多個輔助數(shù)據(jù)結(jié)構(gòu)。

  • Zygote Space
    主要是用來管理Zygote進(jìn)程在啟動過程中預(yù)加載和創(chuàng)建的各種對象,在Zygote Space中不會出發(fā)GC,同時在Zygote進(jìn)程和應(yīng)用程序之間共享Zygote Space。

  • Allocation Space
    在Zygote Space進(jìn)程fork第一個子進(jìn)程之前,會把Zygote Space分為2個部分,原來的已經(jīng)被使用的那部分堆仍舊是Zygote Space,而未使用的那部分堆就叫做Allocation Space,以后的對象都會在Allocation Space上進(jìn)行分配和釋放。Allocation Space不是進(jìn)程間共享的,每個進(jìn)程中都獨立擁有一份。

DVM中數(shù)據(jù)結(jié)構(gòu)如下:

  • Card Table
    用于DVM的并發(fā) GC,當(dāng)?shù)谝淮芜M(jìn)行垃圾標(biāo)記后,記錄垃圾信息。

  • Heap Bitmap
    有2個堆位圖,一個用來記錄上次GC存活的對象,另一個用來記錄這次GC存活的對象。

  • Mark Stack
    DVM的運行時進(jìn)行GC時用來進(jìn)行標(biāo)記的數(shù)據(jù)結(jié)構(gòu)

Android系統(tǒng)的第一個Dalvik虛擬機(jī)是由Zygote進(jìn)程創(chuàng)建的,而其他的應(yīng)用進(jìn)程是由 Zygote 進(jìn)程 fork 出來的,為了盡量地避免拷貝,應(yīng)用程序進(jìn)程使用了一種寫時拷貝技術(shù)(COW)來復(fù)制了Zygote進(jìn)程的地址空間。

Zygote進(jìn)程在啟動過程中創(chuàng)建Dalvik虛擬機(jī)的時候,其實只有一個堆,但是當(dāng)Zygote進(jìn)程在fork第一個應(yīng)用程序進(jìn)程之前,會將已經(jīng)使用了的那部分堆內(nèi)存劃分為一部分,還沒有使用的堆內(nèi)存劃分為另外一部分,前面那部分就是 Zygote Space。

當(dāng)應(yīng)用程序或者Zygote 進(jìn)程想要new一個新的對象的時候就會在 Allocation Space 上分配。每一個應(yīng)用進(jìn)程都會共享一個 Zygote Space,當(dāng)Zygote進(jìn)程或者應(yīng)用程序進(jìn)程對該堆進(jìn)行寫操作時,內(nèi)核就會執(zhí)行真正的拷貝一份提供給進(jìn)程進(jìn)行寫操作。

ART 堆

ART運行時堆劃分為四個空間,分別是Image Space、Zygote Space、Allocation Space和Large Object Space。其中前三個Space是連續(xù)的(Continuous Space),Large Object Space是一些離散地址的集合(Discontinuous Space),用于分配一些大對象。

在Image Space和Zygote Space之間,隔著一段用來映射system@framework@boot.art@classes.oat 文件的內(nèi)存,system@framework@boot.art@classes.oat 是一個OAT文件,它是由在系統(tǒng)啟動類路徑中的所有DEX文件翻譯得到的。

其中,Zygote Space、Allocation Space 跟 Dalvik 中的空間生成方式和含義相同。

  • Image Space
    包含了那些需要預(yù)加載的系統(tǒng)類對象。這意味著需要預(yù)加載的類對象是在生成system@framework@boot.art@classes.oat這個OAT文件的時候創(chuàng)建并且保存在文件system@framework@boot.art@classes.dex中,以后只要系統(tǒng)啟動類路徑中的DEX文件不發(fā)生變化(即不發(fā)生更新升級),那么以后每次系統(tǒng)啟動只需要將文件system@framework@boot.art@classes.dex直接映射到內(nèi)存即可,省去了創(chuàng)建各個類對象的時間。之前使用Dalvik虛擬機(jī)作為應(yīng)用程序運行時時,每次系統(tǒng)啟動時,都需要為那些預(yù)加載的類創(chuàng)建類對象。因此,雖然ART運行時第一次啟動時會比較慢,但是以后啟動實際上會更快。

  • Large Object Space

    • 其中一種實現(xiàn)和Continuous Space的實現(xiàn)類似,預(yù)先分配好一塊大的內(nèi)存空間,然后再在上面為對象分配內(nèi)存塊。不過這種方式實現(xiàn)的Large Object Space不像Continuous Space通過C庫的內(nèi)塊管理接口來分配和釋放內(nèi)存,而是自己維護(hù)一個Free List。每次為對象分配內(nèi)存時,都是從這個Free List找到合適的空閑的內(nèi)存塊來分配。釋放內(nèi)存的時候,也是將要釋放的內(nèi)存添加到該Free List去。

    • 另外一種Large Object Space實現(xiàn)是每次為對象分配內(nèi)存時,都單獨為其映射一新的內(nèi)存。也就是說,為每一個對象分配的內(nèi)存塊都是相互獨立的。這種實現(xiàn)方式相比上面介紹的Free List實現(xiàn)方式,也更簡單一些。在Android 4.4中,ART運行時使用的是后一種實現(xiàn)方式。

對應(yīng)的數(shù)據(jù)結(jié)構(gòu):

  • Mod Union Table
    Mod Union Table 是與Card Table配合使用的,用來記錄在一次GC過程中,記錄不會被回收的Space的對象對會被回收的Space的引用。例如,Image Space的對象對Zygote Space和Allocation Space的對象的引用,以及Zygote Space的對象對Allocation Space的對象的引用。

回收算法

Dalvik

Dalvik 虛擬機(jī)默認(rèn)采用 Mark-Sweep 算法,不會進(jìn)行整理,所以長時間運行會產(chǎn)生碎片問題。但是值得注意的是,在某些情況下,Dalvik 也會采用并發(fā)Gc。


Mark-Sweep 算法步驟

(a)GC 前的狀態(tài)。示例中有一個 GC Root,所有對象都未被標(biāo)記。
(b)GC 標(biāo)記后的狀態(tài)。在標(biāo)記階段,所有活動對象(Active Objects)都會被標(biāo)記。
(c)GC 清除后的狀態(tài)。所有垃圾已被回收,并且所有活動對象的標(biāo)記狀態(tài)都被重置為 false。

垃圾回收原因可以在Dalvik 日志消息中看到:

  • GC_CONCURRENT
    在您的堆開始占用內(nèi)存時可以釋放內(nèi)存的并發(fā)垃圾回收。
  • GC_FOR_MALLOC
    堆已滿而系統(tǒng)不得不停止您的應(yīng)用并回收內(nèi)存時,您的應(yīng)用嘗試分配內(nèi)存而引起的垃圾回收。
  • GC_HPROF_DUMP_HEAP
    當(dāng)您請求創(chuàng)建 HPROF 文件來分析堆時出現(xiàn)的垃圾回收。
  • GC_EXPLICIT
    顯式垃圾回收,例如當(dāng)您調(diào)用 gc() 時(您應(yīng)避免調(diào)用,而應(yīng)信任垃圾回收會根據(jù)需要運行
  • GC_EXTERNAL_ALLOC
    這僅適用于 API 級別 10 及更低級別(更新版本會在 Dalvik 堆中分配任何內(nèi)存)。外部分配內(nèi)存的垃圾回收(例如存儲在原生內(nèi)存或 NIO 字節(jié)緩沖區(qū)中的像素數(shù)據(jù))。

ART

ART 具有可以運行的多種不同的垃圾回收,整個堆回收器會釋放和回收 Image Space 以外的所有其他空間。Art 在GC上不像Dalvik僅有一種回收算法,Art在不同的情況下會選擇不同的回收算法,比如Alloc內(nèi)存不夠的時候會采用非并發(fā)GC,而在Alloc后發(fā)現(xiàn)內(nèi)存達(dá)到一定閥值的時候又會觸發(fā)并發(fā)GC。

Dalvik 和 ART gc 流程對比

Dalvik 垃圾回收 (GC) 可能有損于應(yīng)用性能,從而導(dǎo)致顯示不穩(wěn)定、界面響應(yīng)速度緩慢以及其他問題。ART 通過以下幾種方式對垃圾回收做了優(yōu)化:

  • 只有一次(而非兩次)GC 暫停
  • 在 GC 保持暫停狀態(tài)期間并行處理
  • 在清理最近分配的短時對象這種特殊情況中,回收器的總 GC 時間更短
  • 優(yōu)化了垃圾回收的工效,能夠更加及時地進(jìn)行并行垃圾回收,這使得 GC_FOR_ALLOC 事件在典型用例中極為罕見
  • 壓縮 GC 以減少后臺內(nèi)存使用和碎片

從谷歌提供的數(shù)據(jù)來看,Art相對Dalvik內(nèi)存分配的效率提高了10倍,GC的效率提高了2-3倍。

GC 類型:

  • Concurrent
    不會暫停應(yīng)用線程的并發(fā)垃圾回收。此垃圾回收在后臺線程中運行,而且不會阻止分配。
  • Alloc
    您的應(yīng)用在堆已滿時嘗試分配內(nèi)存引起的垃圾回收。在這種情況下,分配線程中發(fā)生了垃圾回收。可能造成卡頓。
  • Explicit
    由應(yīng)用明確請求的垃圾回收,例如,通過調(diào)用 System#gc()Runtime#gc()。與 Dalvik 相同。不建議使用顯式垃圾回收,因為它們會阻止分配線程并不必要地浪費 CPU 周期。如果顯式垃圾回收導(dǎo)致其他線程被搶占,那么它們也可能會導(dǎo)致卡頓(應(yīng)用中出現(xiàn)間斷、抖動或暫停)。
  • NativeAlloc
    Native 分配(如位圖或 RenderScript 分配對象)導(dǎo)致出現(xiàn)原生內(nèi)存壓力,進(jìn)而引起的回收。
  • CollectorTransition
    由堆轉(zhuǎn)換引起的回收;此回收由運行時切換垃圾回收引起。回收器轉(zhuǎn)換包括將所有對象從空閑列表空間復(fù)制到碰撞指針空間(反之亦然)。當(dāng)前,回收器轉(zhuǎn)換僅在以下情況下出現(xiàn):在 RAM 較小的設(shè)備上,應(yīng)用將進(jìn)程狀態(tài)從可察覺的暫停狀態(tài)變更為可察覺的非暫停狀態(tài)(反之亦然)。
  • HomogeneousSpaceCompact
    齊性空間壓縮是空閑列表空間到空閑列表空間壓縮,通常在應(yīng)用進(jìn)入到可察覺的暫停進(jìn)程狀態(tài)時發(fā)生。這樣做的主要原因是減少 RAM 使用量并對堆進(jìn)行碎片整理。
  • DisableMovingGc
    這不是真正的垃圾回收原因,但請注意,發(fā)生并發(fā)堆壓縮時,由于使用了 GetPrimitiveArrayCritical,回收遭到阻止。一般情況下,強(qiáng)烈建議不要使用 GetPrimitiveArrayCritical,因為它在移動回收器方面具有限制。
  • HeapTrim
    這不是垃圾回收原因,但請注意,堆修剪完成之前回收會一直受到阻止。

參考文章:
官方文檔-內(nèi)存管理概覽
官方文檔-管理應(yīng)用內(nèi)存
官方文檔-進(jìn)程間的內(nèi)存分配
官方文檔-平臺架構(gòu)
官方文檔-調(diào)查 RAM 使用情況
官方文檔-Android Runtime (ART) 和 Dalvik
淺談內(nèi)存映射
android底層之什么是Zram?
官方文檔-Android Runtime (ART) 和 Dalvik
分頁-維基百科
內(nèi)存映射文件-維基百科
Java中什么是JVM及其工作原理?
Android和Linux的關(guān)系
Android LowMemoryKiller原理分析
Android LowMemoryKiller 簡介
Android內(nèi)存管理之LMK和OOM
Java虛擬機(jī)
Android內(nèi)存管理分析總結(jié)
JVM、DVM(Dalvik VM)和ART虛擬機(jī)對比
阿里巴巴 說說 Android 虛擬機(jī)Dalvik與ART區(qū)別在哪里?
Dalvik虛擬機(jī)垃圾收集機(jī)制簡要介紹和學(xué)習(xí)計劃
Dalvik 虛擬機(jī)和 Sun JVM 在架構(gòu)和執(zhí)行方面有什么本質(zhì)區(qū)別?
ART運行時垃圾收集機(jī)制簡要介紹和學(xué)習(xí)計劃
Android 虛擬機(jī) Vs Java 虛擬機(jī)
Android GC原理探究
JVM、DVM(Dalvik VM)和ART虛擬機(jī)對比

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,818評論 6 531
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,185評論 3 414
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,656評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,647評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 71,446評論 6 405
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 54,951評論 1 321
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,041評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,189評論 0 287
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,718評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 40,602評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,800評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,316評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 44,045評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,419評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,671評論 1 281
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,420評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 47,755評論 2 371