Android內(nèi)存管理機制

Linux 的虛擬內(nèi)存、物理內(nèi)存善茎、磁盤

為什么要有虛擬內(nèi)存的概念

  • 進程創(chuàng)建時券册,會分配4G的虛擬內(nèi)存,如果分配物理內(nèi)存的話垂涯,物理內(nèi)存很快就會分配完烁焙。
  • 由于指令都是直接訪問物理內(nèi)存的,那么我這個進程就可以修改其他進程的數(shù)據(jù)耕赘,甚至會修改內(nèi)核地址空間的數(shù)據(jù)骄蝇,這是我們不想看到的。
  • 因為內(nèi)存時隨機分配的操骡,所以程序運行的地址也是不正確的九火。

什么是頁表

進程需要知道哪些地址空間上的數(shù)據(jù)在物理內(nèi)存上,哪些不在(可能這部分存儲在磁盤上)册招,還有在物理內(nèi)存上的哪里岔激,這就需要通過頁表來記錄
頁表的每一個表項分兩部分,第一部分記錄此頁是否在物理內(nèi)存上是掰,第二部分記錄物理內(nèi)存頁的地址(如果在的話)


20180830114901391.png

虛擬內(nèi)存鹦倚、頁表、物理內(nèi)存冀惭、磁盤的工作方式

在進程創(chuàng)建時震叙,內(nèi)核會分配4G虛擬內(nèi)存,當進程沒有開始運行時散休,是不會將磁盤上的程序數(shù)據(jù)和代碼等拷貝到物理內(nèi)存中媒楼,只是建立好虛擬內(nèi)存和磁盤之間的映射關系。

  1. 每次我要訪問地址空間上的某一個地址戚丸,都需要把地址翻譯為實際物理內(nèi)存地址
    所有進程共享這整一塊物理內(nèi)存划址,每個進程只把自己目前需要的虛擬地址空間映射到物理內(nèi)存上
  2. 當進程訪問某個虛擬地址的時候扔嵌,就會先去看頁表,如果發(fā)現(xiàn)對應的數(shù)據(jù)不在物理內(nèi)存上夺颤,就會發(fā)生缺頁異常
  3. 缺頁異常的處理過程痢缎,操作系統(tǒng)立即阻塞該進程,并將硬盤里對應的頁換入內(nèi)存世澜,然后使該進程就緒独旷,如果內(nèi)存已經(jīng)滿了,沒有空地方了寥裂,那就找一個頁覆蓋嵌洼,至于具體覆蓋的哪個頁,就需要看操作系統(tǒng)的頁面置換算法是怎么設計的了封恰。
20180830111258836.png

mmap

在LINUX中我們可以使用mmap用來在進程虛擬內(nèi)存地址空間中分配地址空間麻养,創(chuàng)建和物理內(nèi)存的映射關系。

映射關系

映射的內(nèi)容

  • 文件映射: 磁盤文件映射進程的虛擬地址空間诺舔,第一次使用時使用文件內(nèi)容初始化物理內(nèi)存鳖昌。
  • 匿名映射:初始化全為0的內(nèi)存空間。

映射是否共享

  • 私有映射(MAP_PRIVATE) : 多進程間數(shù)據(jù)共享低飒,修改不反應到磁盤實際文件遗遵,是一個copy-on-write(寫時復制)的映射方式。
  • 共享映射(MAP_SHARED): 多進程間數(shù)據(jù)共享逸嘀,修改反應到磁盤實際文件中车要。

總共4種映射關系

  1. 私有文件映射:
    多個進程使用同樣的物理內(nèi)存頁進行初始化,但是各個進程對內(nèi)存文件的修改不會共享崭倘,也不會反應到物理文件中

  2. 私有匿名映射:
    mmap會創(chuàng)建一個新的映射翼岁,各個進程不共享,這種使用主要用于分配內(nèi)存(malloc分配大內(nèi)存會調(diào)用mmap)司光。
    例如開辟新進程時琅坡,會為每個進程分配虛擬的地址空間,這些虛擬地址映射的物理內(nèi)存空間各個進程間讀的時候共享残家,寫的時候會copy-on-write榆俺。

  3. 共享文件映射:
    多個進程通過虛擬內(nèi)存技術共享同樣的物理內(nèi)存空間,對內(nèi)存文件 的修改會反應到實際物理文件中坞淮,他也是進程間通信(IPC)的一種機制茴晋。

  4. 共享匿名映射:
    這種機制在進行fork的時候不會采用寫時復制,父子進程完全共享同樣的物理內(nèi)存頁回窘,這也就實現(xiàn)了父子進程通信(IPC).

mmap和new/malloc 的關系

new/malloc 是C++/C語言層面的事情诺擅,實際在類linux的操作系統(tǒng)層面,給用戶提供的申請內(nèi)存的函數(shù)只有brk/sbrk和mmap函數(shù)啡直。

  • sbrk 作用就是擴展heap的上界烁涌,可以傳入一個分配大小苍碟,并返回新的brk的地址。
  • Malloc函數(shù)當申請小內(nèi)存時使用sbrk來分配內(nèi)存撮执,大內(nèi)存則使用mmap申請微峰,如果是這種情況,這時的malloc并沒有申請物理內(nèi)存的占用 抒钱。但實際上大部分malloc的實現(xiàn)都會在操作系統(tǒng)內(nèi)再維護一個內(nèi)存池蜓肆,它會預先申請一塊較大的連續(xù)內(nèi)存復用,最終都是走的mmap继效。
QQ圖片20200904163539.png

mmap在write和read時會發(fā)生什么

write

  1. 進程(用戶態(tài))將需要寫入的數(shù)據(jù)直接copy到對應的mmap地址(內(nèi)存copy)
  2. 若mmap地址未對應物理內(nèi)存症杏,則產(chǎn)生缺頁異常装获,由內(nèi)核處理
  3. 若已對應瑞信,則直接copy到對應的物理內(nèi)存
  4. 由操作系統(tǒng)調(diào)用,將臟頁回寫到磁盤(通常是異步的)

read

image.png

從圖中可以看出穴豫,mmap要比普通的read系統(tǒng)調(diào)用少了一次copy的過程凡简。因為read調(diào)用,進程是無法直接訪問kernel space的精肃,所以在read系統(tǒng)調(diào)用返回前秤涩,內(nèi)核需要將數(shù)據(jù)從內(nèi)核復制到進程指定的buffer。但mmap之后司抱,進程可以直接訪問mmap的數(shù)據(jù)(page cache)筐眷。

mmap 性能總結

優(yōu)點

  • 對文件的讀取操作跨過了頁緩存,減少了數(shù)據(jù)的拷貝次數(shù)习柠,用內(nèi)存讀寫取代I/O讀寫匀谣,提高了文件讀取效率。

  • 實現(xiàn)了用戶空間和內(nèi)核空間的高效交互方式资溃。兩空間的各自修改操作可以直接反映在映射的區(qū)域內(nèi)武翎,從而被對方空間及時捕捉。

  • 提供進程間共享內(nèi)存及相互通信的方式溶锭。不管是父子進程還是無親緣關系的進程宝恶,都可以將自身用戶空間映射到同一個文件或匿名映射到同一片區(qū)域。從而通過各自對映射區(qū)域的改動趴捅,達到進程間通信和進程間共享的目的垫毙。同時,如果進程A和進程B都映射了區(qū)域C拱绑,當A第一次讀取C時通過缺頁從磁盤復制文件頁到內(nèi)存中露久;但當B再讀C的相同頁面時,雖然也會產(chǎn)生缺頁異常欺栗,但是不再需要從磁盤中復制文件過來毫痕,而可直接使用已經(jīng)保存在內(nèi)存中的文件數(shù)據(jù)征峦。

  • 可用于實現(xiàn)高效的大規(guī)模數(shù)據(jù)傳輸。內(nèi)存空間不足消请,是制約大數(shù)據(jù)操作的一個方面栏笆,解決方案往往是借助硬盤空間協(xié)助操作,補充內(nèi)存的不足臊泰。但是進一步會造成大量的文件I/O操作蛉加,極大影響效率。這個問題可以通過mmap映射很好的解決缸逃。換句話說针饥,但凡是需要用磁盤空間代替內(nèi)存的時候,mmap都可以發(fā)揮其功效需频。

缺點

  • 文件如果很小丁眼,是小于4096字節(jié)的,比如10字節(jié)昭殉,由于內(nèi)存的最小粒度是頁苞七,而進程虛擬地址空間和內(nèi)存的映射也是以頁為單位。雖然被映射的文件只有10字節(jié)挪丢,但是對應到進程虛擬地址區(qū)域的大小需要滿足整頁大小蹂风,因此mmap函數(shù)執(zhí)行后,實際映射到虛擬內(nèi)存區(qū)域的是4096個字節(jié)乾蓬,11~4096的字節(jié)部分用零填充惠啄。因此如果連續(xù)mmap小文件,會浪費內(nèi)存空間任内。
  • 對變長文件不適合撵渡,文件無法完成拓展,因為mmap到內(nèi)存的時候族奢,你所能夠操作的范圍就確定了姥闭。
  • 如果更新文件的操作很多,會觸發(fā)大量的臟頁回寫及由此引發(fā)的隨機IO上越走。所以在隨機寫很多的情況下棚品,mmap方式在效率上不一定會比帶緩沖區(qū)的一般寫快。

進程中的內(nèi)存

android 系統(tǒng)給app進程分配的的內(nèi)存都有上限廊敌,如果需要增大內(nèi)存限制铜跑,有以下方案:

  • 則在AndroidManifest.xml中的application標簽加上 android:largeHeap="true"
  • 創(chuàng)建子進程。創(chuàng)建一個新的進程骡澈,那么我們就可以把一些對象分配到新進程的heap上了锅纺,從而達到一個應用程序使用更多的內(nèi)存的目的。
  • 使用jni在native heap上申請空間(推薦使用)肋殴。nativeheap的增長并不受dalvik vm heapsize的限制囤锉。只要RAM有剩余空間坦弟,程序員可以一直在native heap上申請空間,當然如果 RAM快耗盡官地,memory killer會殺進程釋放RAM酿傍。
  • 使用顯存。使用 OpenGL textures 等 API 驱入, texture memory 不受 dalvik vm heapsize 限制赤炒。

內(nèi)存示例(圖中的所占內(nèi)存都是指物理內(nèi)存)

adb shell dumpsys meminfo [包名]


企業(yè)微信截圖_3a99991d-5dac-432d-a168-90395d55d651.png
企業(yè)微信截圖_3524d35d-e233-43bc-96ce-b04fa4692f82.png

名詞解釋

  • Pss(proportional[比例的] set size)Total:進程實際占用的物理內(nèi)存大小,但是android內(nèi)存涉及到在系統(tǒng)中同其他進程共享一部分庫(可能是so亏较,可能是字體文件等的mmap)莺褒,所以這里面會考慮這個因素計算你的進程平攤的這部分共享庫的大小,這里的p就意味著統(tǒng)計了這個平攤后的物理內(nèi)存雪情。這是衡量進程占用內(nèi)存的最真實指標遵岩。

  • Private Dirty 和 Private Clean:則是完全該進程自己(而不包括和別人共享部分)占用的物理內(nèi)存,clean是指那部分可能被swap的內(nèi)存部分(即前面說到的擁有backup文件旺罢,mmap之后一直保持只讀狀態(tài)旷余,他們具有被swap out的可能绢记,比如你的so庫文件扁达。),而dirty部分就是除clean之外那些不能被swap的內(nèi)存蠢熄。Private Dirty通常是你的進程內(nèi)存最需要優(yōu)化的地方跪解,因為他們是大頭。

  • Swapped Dirty:指的并不是被swap-out出去的內(nèi)存签孔,而是android系統(tǒng)中zram機制壓縮掉的部分Private Dirty部分的不常用物理內(nèi)存叉讥,這個重要度等同于Private Dirty,因為哪些Private Dirty會被壓縮不能被控制饥追,所以這部分多通常也是Private Dirty 的图仓。

  • Native Heap:這是C/C++層直接通過malloc分配的內(nèi)存

  • Dalvik Heap:虛擬機堆,由java層new出來的對象放在這里

  • Dalvik other:但绕?救崔??

  • Stack: 棧分配內(nèi)存

  • Ashmem: 進程的匿名共享內(nèi)存 Anonymous Shared Memory 捏顺,通常不會很大六孵,和操作系統(tǒng)有關系

  • GFX dev :通俗來說是你的顯存,android 顯存和內(nèi)存在同樣的物理設備上幅骄,所以統(tǒng)計的總內(nèi)存是包括顯存

  • Other Dev: 除顯卡外所有其他硬件設備的mmap后的物理內(nèi)存劫窒,可能包括聲卡等,通常不多拆座。

  • .so mmap: 這個就是so庫本身文件mmap占用的物理內(nèi)存主巍,我們隨著游戲進度會逐漸的讀取我們的so文件冠息,造成和缺頁的部分就是在物理內(nèi)存產(chǎn)生占用,這部分大就是so庫太大了孕索,但是這部分因為有很多是readonly的mmap铐达,所以有更大的機會被swap-out出去。

  • .apk .dex oat .art mmap: 這些都是android 程序文件本身被mmap占用的內(nèi)存檬果,和so的性質差不多瓮孙。

  • Other mmap: 是所有除了上面的之外其他的所有非匿名方式的mmap

  • Unkonwn: 所有的匿名mmap

注意點:

  • PSS中已經(jīng)包含了Private Dirty和Private Clean,但是沒包含swapped dity选脊,所以最終衡量你的進程對物理內(nèi)存的占用應該是取PSS+Swapped Dirty

JAVA 虛擬機進程中的內(nèi)存結構

2718191-c237aac8fe972243.png
  • 程序計數(shù)器(Program Counter Register):

    • 是一塊較小的內(nèi)存空間杭抠,它可以看作是當前線程所執(zhí)行的Java字節(jié)碼的行號指示器。在虛擬機的概念模型里恳啥,字節(jié)碼解釋器工作時就是通過改變這個計數(shù)器的值來選取下一條需要執(zhí)行的字節(jié)碼指令偏灿,分支、循環(huán)钝的、跳轉翁垂、異常處理、線程恢復等基礎功能都需要依賴這個計數(shù)器來完成硝桩。
    • 由于Java虛擬機的多線程是通過線程輪流切換并分配處理器執(zhí)行時間的方式來實現(xiàn)的沿猜,在任何一個確定的時刻,一個處理器都只會執(zhí)行一條線程中的指令碗脊。因此啼肩,為了線程切換后能恢復到正確的執(zhí)行位置,每條線程都需要有一個獨立的程序計數(shù)器衙伶,各條線程之間計數(shù)器互不影響祈坠,獨立存儲,我們稱這類內(nèi)存區(qū)域為“線程私有”的內(nèi)存矢劲。
    • 如果線程正在執(zhí)行的是一個Java方法赦拘,這個計數(shù)器記錄的是正在執(zhí)行的虛擬機字節(jié)碼指令的地址;如果正在執(zhí)行的是Native方法芬沉,這個計數(shù)器值則為空(Undefined)躺同。此內(nèi)存區(qū)域是唯一 一個在Java虛擬機規(guī)范中沒有規(guī)定OutOfMemoryError情況的區(qū)域。
  • Java虛擬機棧

    • 與程序計數(shù)器一樣花嘶,Java虛擬機棧(Java Virtual Machine Stacks)也是線程私有的笋籽,它的生命周期與線程相同。虛擬機棧描述的是Java方法執(zhí)行的內(nèi)存模型:每個方法在執(zhí)行的同時都會創(chuàng)建一個棧幀(Stack Frame)用于存儲局部變量表椭员、操作數(shù)棧车海、動態(tài)鏈接、方法出口等信息。每一個方法從調(diào)用直至執(zhí)行完成的過程侍芝,就對應著一個棧幀在虛擬機棧中入棧到出棧的過程研铆。
    • 經(jīng)常有人把Java內(nèi)存區(qū)分為堆內(nèi)存(Heap)和棧內(nèi)存(Stack),其中所指的“堆”就是Java堆州叠,而所指的“椏煤欤”就是現(xiàn)在所講的虛擬機棧,或者說是虛擬機棧中局部變量表部分咧栗。
    • 局部變量表存放了編譯期可知的各種基本數(shù)據(jù)類型(boolean逆甜、byte、char致板、short交煞、int、float斟或、long素征、double)、對象引用(reference類型萝挤,它不等同于對象本身御毅,可能是一個指向對象起始地址的引用指針,也可能是指向一個代表對象的句柄或其他與此對象相關位置)和returnAddress類型(指向了一條字節(jié)碼指令的地址)怜珍。
    • 其中64為長度的long和double類型的數(shù)據(jù)會占用2個局部變量空間(Slot)端蛆,其余的數(shù)據(jù)類型只占用1個。局部變量表所需的內(nèi)存空間在編譯期間完成分配绘面,當進入一個方法時欺税,這個方法需要在幀中分配多大的局部變量空間是完全確定的侈沪,在方法運行期間不會改變局部變量表的大小揭璃。
    • 在Java虛擬機規(guī)范中,對這個區(qū)域規(guī)定了兩種異常狀況:如果線程請求的棧深度大于虛擬機所允許的深度亭罪,將拋出StackOverflowError異常瘦馍;如果虛擬機棧可以動態(tài)擴展(當前大部分的Java虛擬機都可動態(tài)擴展应役,只不過Java虛擬機規(guī)范中也允許固定長度的虛擬機棧)情组,如果擴展時無法申請到足夠的內(nèi)存,就會拋出OutOfMemoryError異常箩祥。
  • 本地方法棧

    • 本地方法棧(Native Method Stack)與虛擬機棧所發(fā)揮的作用是非常相似的院崇,它們之間的區(qū)別不過是虛擬機棧為虛擬機執(zhí)行Java方法(也就是字節(jié)碼)服務,而本地方法棧則為虛擬機使用到的Native方法服務袍祖。在虛擬機規(guī)范中對本地方法棧中方法使用的語言底瓣、使用方式與數(shù)據(jù)結構并沒有強制規(guī)定。HotSpot虛擬機直接把本地方法棧和虛擬機棧合二為一蕉陋。與虛擬機棧一樣捐凭,本地方法棧區(qū)域也會拋出StackOverflowError和OutOfMemoryError異常拨扶。
  • Java堆

    • 對于大多數(shù)應用來說,Java堆(Java Heap)是Java虛擬機所管理的內(nèi)存中最大的一塊茁肠。Java堆是被所有線程共享的一塊內(nèi)存區(qū)域患民,在虛擬機啟動時創(chuàng)建。此內(nèi)存區(qū)域的唯一目的就是存放對象實例垦梆,幾乎所有的對象實例都在這里分配內(nèi)存匹颤。這一點在Java虛擬機規(guī)范中的描述是:所有的對象實例以及數(shù)組都要在堆上分配,但是隨著JIT編譯器的發(fā)展以及逃逸分析技術逐漸成熟托猩,棧上分配惋嚎、標量替換優(yōu)化技術將會導致一些微妙的變化發(fā)生,所有的對象都分配在堆上也漸漸變得不是那么“絕對”了站刑。
    • Java堆是垃圾收集器管理的主要區(qū)域另伍,因此很多時候也被稱做“GC堆”(Garbage Collected Heap)。從內(nèi)存回收的角度來看绞旅,由于現(xiàn)在收集器基本都采用分代收集算法摆尝,所以Java堆中還可以細分為:新生代和老年代;再細致一點的因悲,新生代可以有Eden空間堕汞、From Survivor空間、To Survivor空間等晃琳。從內(nèi)存分配的角度來看讯检,線程共享的Java堆中可能劃分出多個線程私有的分配緩沖區(qū)(Thread Local Allocation Buffer,TLAB)卫旱。不過無論如何劃分人灼,都與存放內(nèi)容無關,無論哪個區(qū)域顾翼,存儲的都仍然是對象實例投放,進一步劃分的目的是為了更好地回收內(nèi)存,或者更快地分配內(nèi)存适贸。
    • 根據(jù)Java虛擬機規(guī)范的規(guī)定灸芳,Java堆可以處于物理上不連續(xù)的內(nèi)存空間中,只要邏輯上是連續(xù)的即可拜姿,就像我們的磁盤空間一樣烙样。在實現(xiàn)時,既可以實現(xiàn)成固定大小的蕊肥,也可以是擴展的谒获,不過當前主流的虛擬機都是按照可擴展來實現(xiàn)的(通過-Xmx和-Xms控制)。如果在堆中沒有內(nèi)存完成實例分配,并且堆也無法再擴展時究反,將會拋出OutOfMemoryError異常寻定。
  • 方法區(qū) (永久代)
    方法區(qū)(Method Area)與Java堆一樣,是各個線程共享的內(nèi)存區(qū)域精耐,它用于存儲已被虛擬機加載的類信息狼速、常量、靜態(tài)變量卦停、即時編譯器編譯后的代碼等數(shù)據(jù)向胡,即存放靜態(tài)文件,如Java類惊完、方法等僵芹。雖然Java虛擬機規(guī)范把方法區(qū)描述為堆的一個邏輯部分,但是它卻有一個別名叫做Non-Heap(非堆)小槐,目的應該是與Java堆區(qū)分開來拇派。

    • jdk7 以前的實現(xiàn)如下:


      1217276-20190418165715298-666699733.png
    • jdk7:
      在目前已經(jīng)發(fā)布的JDK1.7的HotSpot中,已經(jīng)把原本放在永久代的字符串常量池移到了Java堆中凿跳。
    • jdk8:
      jdk8版本中則把永久代給完全刪除了当窗,取而代之的是MetaSpace垢箕。
      運行時常量池和靜態(tài)變量都存儲到了堆中悍募,MetaSpace存儲類的元數(shù)據(jù)蒜茴,MetaSpace直接在本地內(nèi)存中(Native memory),這樣類的元數(shù)據(jù)分配只受本地內(nèi)存大小的限制疆栏,OOM問題就不存在了曾掂。


      1217276-20190418171349818-952181838.png
  • 運行時常量池

    • 運行時常量池(Runtime Constant Pool)是方法區(qū)的一部分。Class文件中除了有類的版本壁顶、字段珠洗、方法、接口等描述信息外博助,還有一項信息是常量池(Constant Pool Table)险污,用于存放編譯期生成的各種字面量和符號引用,這部分內(nèi)容將在類加載后進入方法區(qū)的運行時常量池中存放富岳。
    • 運行時常量池相對于Class文件常量池的另外一個重要特征是具備動態(tài)性,Java語言并不要求常量一定只有編譯期才能產(chǎn)生拯腮,也就是并非預置入Class文件中常量池的內(nèi)容才能進入方法區(qū)運行時常量池窖式,運行期間也可能將新的常量放入池中,這種特性被開發(fā)人員利用得比較多的便是String類的intern()方法动壤。
    • 既然運行時常量池是方法區(qū)的一部分萝喘,自然受到方法區(qū)內(nèi)存的限制,當常量池無法再申請到內(nèi)存時會拋出OutOfMemoryError異常。
  • 直接內(nèi)存(堆外內(nèi)存)

    • 直接內(nèi)存(Direct Memory)阁簸,也叫堆外內(nèi)存爬早,它并不是虛擬機運行時數(shù)據(jù)區(qū)的一部分,也不是Java虛擬機規(guī)范中定義的內(nèi)存區(qū)域启妹,而是Java虛擬機的堆以外的內(nèi)存筛严,直接受操作系統(tǒng)管理。但是這部分內(nèi)存也被頻繁地使用饶米,而且也可能導致OutOfMemoryError異常出現(xiàn)桨啃。使用堆外內(nèi)存有兩個優(yōu)勢,一是減少了垃圾回收檬输,二是提升復制速度照瘾,如NIO就是采用堆外內(nèi)存∩ゴ龋可以使用未公開的Unsafe和NIO包下ByteBuffer來創(chuàng)建堆外內(nèi)存析命。
    • 在JDK1.4中新加入了NIO(New Input/Output)類,引入了一種基于通道(Channel)與緩沖區(qū)(Buffer)的I/O方式逃默,它可以使用Native函數(shù)庫直接分配堆外內(nèi)存碳却,然后通過一個存儲在Java堆中的DirectByteBuffer對象作為這塊內(nèi)存的引用進行操作。這樣能在一些場景中顯著提高性能笑旺,因為避免了在Java堆和Native堆中來回復制數(shù)據(jù)昼浦。
    • 顯然,本機直接內(nèi)存的分配不會受到Java堆大小的限制筒主,但是关噪,既然是內(nèi)存,肯定還是會受到本機總內(nèi)存(包括RAM以及SWAP區(qū)或者分頁文件)大小以及處理尋址空間的限制乌妙。服務器管理員在配置虛擬機參數(shù)時使兔,會根據(jù)實際內(nèi)存設置-Xmx等參數(shù)信息,但經(jīng)常忽略直接內(nèi)存藤韵,使得各個內(nèi)存區(qū)域總和大于物理內(nèi)存限制(包括物理的和操作系統(tǒng)級的限制)虐沥,從而導致動態(tài)擴展時出現(xiàn)OutOfMemoryError異常。

android LowMemoryKiller原理

進程的優(yōu)先級

前臺進程(Foreground process)

  • 包含正在交互的Activity(resumed
  • 包含綁定到正在交互的Activity的Service
  • 包含正在“前臺”運行的Service(服務已調(diào)用startForeground())
  • 包含正執(zhí)行一個生命周期回調(diào)的Service(onCreate()泽艘、onStart() 或 onDestroy())
  • 包含一個正執(zhí)行其onReceive()方法的BroadcastReceiver

可見進程(Visible process)

  • 包含不在前臺欲险、但仍對用戶可見的 Activity(已調(diào)用其 onPause() 方法)。例如匹涮,如果前臺 Activity 啟動了一個對話框天试,允許在其后顯示上一Activity,則有可能會發(fā)生這種情況然低。
  • 包含綁定到可見(或前臺)Activity 的 Service喜每。

服務進程(Service process)

  • 正在運行已使用 startService() 方法啟動的服務且不屬于上述兩個更高類別進程的進程务唐。盡管服務進程與用戶所見內(nèi)容沒有直接關聯(lián),但是它們通常在執(zhí)行一些用戶關心的操作(例如带兜,在后臺播放音樂或從網(wǎng)絡下載數(shù)據(jù))枫笛。因此,除非內(nèi)存不足以維持所有前臺進程和可見進程同時運行刚照,否則系統(tǒng)會讓服務進程保持運行狀態(tài)刑巧。

后臺進程(Background process)

  • 包含目前對用戶不可見的 Activity 的進程(已調(diào)用 Activity 的 onStop() 方法)。這些進程對用戶體驗沒有直接影響涩咖,系統(tǒng)可能隨時終止它們海诲,以回收內(nèi)存供前臺進程、可見進程或服務進程使用檩互。 通常會有很多后臺進程在運行特幔,因此它們會保存在 LRU (最近最少使用)列表中,以確保包含用戶最近查看的 Activity 的進程最后一個被終止闸昨。如果某個 Activity 正確實現(xiàn)了生命周期方法蚯斯,并保存了其當前狀態(tài),則終止其進程不會對用戶體驗產(chǎn)生明顯影響饵较,因為當用戶導航回該 Activity 時拍嵌,Activity會恢復其所有可見狀態(tài)。 有關保存和恢復狀態(tài)循诉、或者異常殺死恢復可以參考前兩篇 文章横辆。

空進程(Empty process)

  • 不含任何活動應用組件的進程。保留這種進程的的唯一目的是用作緩存茄猫,以縮短下次在其中運行組件所需的啟動時間狈蚤,這就是所謂熱啟動 。為了使系統(tǒng)資源在進程緩存和底層內(nèi)核緩存之間保持平衡划纽,系統(tǒng)往往會終止這些進程脆侮。

Android進程的優(yōu)先級是如何更新的

這里是通過了Linux中的一個proc文件體統(tǒng),proc文件系統(tǒng)可以簡單的看多是內(nèi)核空間映射成用戶可以操作的文件系統(tǒng)勇劣,當然不是所有進程都有權利操作靖避,通過proc文件系統(tǒng),用戶空間的進程就能夠修改內(nèi)核空間的數(shù)據(jù)比默,比如修改進程的優(yōu)先級幻捏。

version 5.0之前

AMS進程直接修改proc,

version 5.0之后

修改優(yōu)先級的操作被封裝成了一個獨立的服務-lmkd退敦,lmkd服務位于用戶空間粘咖,其作用層次同AMS、WMS類似侈百,就是一個普通的系統(tǒng)服務瓮下。AMS 和 lmkd 通過socket通信。

Android 系統(tǒng)是如何回調(diào)低內(nèi)存方法

  • kswapd的內(nèi)核線程钝域,當linux回收存放分頁的時候讽坏,kswapd線程將會遍歷一張shrinker鏈表,并執(zhí)行回調(diào)例证,或者某個app啟動路呜,發(fā)現(xiàn)可用內(nèi)存不足時,則內(nèi)核會阻塞請求分配內(nèi)存的進程分配內(nèi)存的過程织咧,并在該進程中去執(zhí)行l(wèi)owmemorykiller來釋放內(nèi)存

Android進程如何被殺死

  • LomemoryKiller屬于一個內(nèi)核驅動模塊胀葱,主要功能是:在系統(tǒng)內(nèi)存不足的時候掃描進程隊列,找到低優(yōu)先級(也許說性價比低更合適)的進程并殺死笙蒙,以達到釋放內(nèi)存的目的抵屿。

被動掃描。找到低優(yōu)先級的進程殺死捅位。

refrence

https://blog.csdn.net/c10WTiybQ1Ye3/article/details/107723950

虛擬內(nèi)存與物理內(nèi)存的聯(lián)系與區(qū)別
[關于 mmap解析](http://www.reibang.com/p/755338d11865
[Android性能優(yōu)化-內(nèi)存篇](http://www.reibang.com/p/829477754c19
UE高級性能剖析技術(三)-- Android內(nèi)存分布和優(yōu)化
【騰訊優(yōu)測干貨分享】如何降低App的待機內(nèi)存(四)——進階:內(nèi)存原理

# Java虛擬機—Java8內(nèi)存模型(整理版)

?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末轧葛,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子艇搀,更是在濱河造成了極大的恐慌尿扯,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,948評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件焰雕,死亡現(xiàn)場離奇詭異衷笋,居然都是意外死亡,警方通過查閱死者的電腦和手機矩屁,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,371評論 3 385
  • 文/潘曉璐 我一進店門辟宗,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人档插,你說我怎么就攤上這事慢蜓。” “怎么了郭膛?”我有些...
    開封第一講書人閱讀 157,490評論 0 348
  • 文/不壞的土叔 我叫張陵晨抡,是天一觀的道長。 經(jīng)常有香客問我则剃,道長耘柱,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,521評論 1 284
  • 正文 為了忘掉前任棍现,我火速辦了婚禮调煎,結果婚禮上,老公的妹妹穿的比我還像新娘己肮。我一直安慰自己士袄,他們只是感情好悲关,可當我...
    茶點故事閱讀 65,627評論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著娄柳,像睡著了一般寓辱。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上赤拒,一...
    開封第一講書人閱讀 49,842評論 1 290
  • 那天秫筏,我揣著相機與錄音,去河邊找鬼挎挖。 笑死这敬,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的蕉朵。 我是一名探鬼主播崔涂,決...
    沈念sama閱讀 38,997評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼墓造!你這毒婦竟也來了堪伍?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 37,741評論 0 268
  • 序言:老撾萬榮一對情侶失蹤觅闽,失蹤者是張志新(化名)和其女友劉穎帝雇,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體蛉拙,經(jīng)...
    沈念sama閱讀 44,203評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡尸闸,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,534評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了孕锄。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片吮廉。...
    茶點故事閱讀 38,673評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖畸肆,靈堂內(nèi)的尸體忽然破棺而出宦芦,到底是詐尸還是另有隱情,我是刑警寧澤轴脐,帶...
    沈念sama閱讀 34,339評論 4 330
  • 正文 年R本政府宣布调卑,位于F島的核電站,受9級特大地震影響大咱,放射性物質發(fā)生泄漏恬涧。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,955評論 3 313
  • 文/蒙蒙 一碴巾、第九天 我趴在偏房一處隱蔽的房頂上張望溯捆。 院中可真熱鬧,春花似錦厦瓢、人聲如沸提揍。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,770評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽碳锈。三九已至顽冶,卻和暖如春欺抗,著一層夾襖步出監(jiān)牢的瞬間售碳,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,000評論 1 266
  • 我被黑心中介騙來泰國打工绞呈, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留贸人,地道東北人。 一個月前我還...
    沈念sama閱讀 46,394評論 2 360
  • 正文 我出身青樓佃声,卻偏偏與公主長得像艺智,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子圾亏,可洞房花燭夜當晚...
    茶點故事閱讀 43,562評論 2 349