Android | App內(nèi)存優(yōu)化 之 JVM & Android內(nèi)存管理機(jī)制及GC機(jī)制 (拓展.GCRoot)

Java內(nèi)存分配

圖自慕課網(wǎng)
方法區(qū):
  • 又叫靜態(tài)區(qū),與Java堆一樣,是所有線程共享的內(nèi)存區(qū)域。?。。?!
    方法區(qū)包含所有的class文件static變量/方法?。?!

  • 方法區(qū)中包含的都是在整個(gè)程序中永遠(yuǎn)唯一的元素,如class,static變量。

  • 用于存儲
    已被虛擬機(jī)加載的
    類信息、常量、靜態(tài)變量即時(shí)編譯器編譯后代碼/Java Class文件等數(shù)據(jù)。

  • 人們更愿意把這個(gè)區(qū)域稱為“永久代”(Permanent Generation),
    在發(fā)布的JDK1.7的HotSpot中,已經(jīng)把原本放在永久代的字符串常量池移出。
    它還有個(gè)別名叫做Non-Heap(非堆)。

  • 除了和Java堆一樣,
    不需要連續(xù)的內(nèi)存
    可以選擇固定大小可擴(kuò)展外,
    還可選擇不實(shí)現(xiàn)GC。

  • Java虛擬機(jī)規(guī)范中,
    當(dāng)方法區(qū)無法滿足內(nèi)存分配需求時(shí),將拋出OutOfMemoryError異常。




  • 每個(gè)線程包含一個(gè)棧區(qū),
    棧中只保存基礎(chǔ)數(shù)據(jù)類型對象引用及其對應(yīng)的以及基礎(chǔ)數(shù)據(jù)引用
    (Java語言提供了八種基本數(shù)據(jù)類型
    六種數(shù)字類型(四個(gè)整數(shù)型long、int、short、byte,兩個(gè)浮點(diǎn)型float、double),
    一種字符類型String,還有一種布爾型

  • 每個(gè)棧中的數(shù)據(jù)(基礎(chǔ)數(shù)據(jù)類型對象引用)都是私有的,
    其他棧不能訪問。

  • 棧分為3個(gè)部分:
    基本類型變量區(qū)、執(zhí)行環(huán)境上下文、操作指令區(qū)(存放操作指令)。



虛擬機(jī)棧
  • 每個(gè)方法在執(zhí)行的同時(shí)都會(huì)創(chuàng)建一個(gè)棧幀,
    用于存儲局部變量表、操作數(shù)棧、動(dòng)態(tài)鏈接、方法出口等信息。

  • 每一個(gè)方法調(diào)用直至執(zhí)行完成的過程,
    就對應(yīng)著一個(gè)棧幀在虛擬機(jī)棧中入棧出棧的過程。

局部變量表存放了編譯期可知的
各種基本數(shù)據(jù)類型、對象引用類型returnAddress類型,
它所需的內(nèi)存空間在編譯期間完成分配。

  • 線程私有的內(nèi)存,與線程生命周期相同。!?。。?/strong>

  • 一般把Java內(nèi)存區(qū)分為堆內(nèi)存(Heap)棧內(nèi)存(Stack),
    其中『棧』指的是虛擬機(jī)棧,『堆』指的是Java堆。

  • Java虛擬機(jī)規(guī)范中,對這個(gè)區(qū)域規(guī)定了兩種異常狀況:

    • 如果線程請求棧深度大于虛擬機(jī)所允許的深度
      將拋出StackOverflowError異常;

    • 如果虛擬機(jī)棧可動(dòng)態(tài)擴(kuò)展擴(kuò)展時(shí)無法申請到足夠的內(nèi)存,
      將拋出OutOfMemoryError異常。



本地方法棧
  • 存儲局部變量表、操作數(shù)棧等;

  • 是虛擬機(jī)使用到的Native方法服務(wù)。
    虛擬機(jī)規(guī)范中,對這個(gè)區(qū)域無強(qiáng)制規(guī)定,由具體的虛擬機(jī)自由實(shí)現(xiàn)。
    與虛擬機(jī)棧一樣,
    本地方法棧區(qū)域也會(huì)拋出StackOverflowErrorOutOfMemoryError異常。

  • 虛擬機(jī)棧是為Java方法服務(wù)的;
    本地方法棧是為Native方法服務(wù)的;



  • 當(dāng)然還要注意String的特殊性

    • 一個(gè)例子:
    • 還有一例:




  • 存儲的全部是對象,
    每個(gè)對象都包含一個(gè)與之對應(yīng)的class的信息。
    (class的目的是得到操作指令)

  • jvm只有一個(gè)堆區(qū)(heap)被所有線程共享,堆中不存放基本類型和對象引用,只存放對象本身

  • 被所有線程共享的一塊內(nèi)存區(qū)域,在虛擬機(jī)啟動(dòng)時(shí)創(chuàng)建;
    包含一切new出來的對象

  • 每一個(gè)對象的實(shí)際分配內(nèi)存都是在上進(jìn)行分配的;
    用于存放幾乎所有的對象實(shí)例和數(shù)組。

在Java堆中,
可能劃分出多個(gè)線程私有分配緩沖區(qū)(Thread Local Allocation Buffer,TLAB),
但無論哪個(gè)區(qū)域,存儲的都仍然是對象實(shí)例
進(jìn)一步劃分的目的是
為了更好地回收內(nèi)存,
或者更快地分配內(nèi)存。

  • 虛擬機(jī)棧中,分配的只是引用,
    虛擬機(jī)棧當(dāng)中的引用,會(huì)指向在真正創(chuàng)建的對象;

  • GC主要作用、管理區(qū)域,因?yàn)樗純?nèi)存最大,最有可能產(chǎn)生垃圾,也被稱做“GC堆”;
    經(jīng)常說的內(nèi)存泄漏也是發(fā)生在此區(qū)域;

  • 是Java虛擬機(jī)所管理的內(nèi)存最大的一塊

  • 可處于物理上不連續(xù)內(nèi)存空間中,只要邏輯上連續(xù)的即可。

  • Java虛擬機(jī)規(guī)范中,
    如果在堆中沒有內(nèi)存完成實(shí)例分配,且堆也無法再擴(kuò)展時(shí),
    將會(huì)拋出OutOfMemoryError異常。




程序計(jì)數(shù)器(Program Counter Register)
  • 當(dāng)前線程所執(zhí)行的字節(jié)碼行號指示器
    • 如果線程正在執(zhí)行的是一個(gè)Java方法,
      那么計(jì)數(shù)器記錄的是
      正在執(zhí)行虛擬機(jī)字節(jié)碼指令地址

    • 如果線程正在執(zhí)行的是一個(gè)Native方法,
      那么計(jì)數(shù)器的值則為

**注意:?。。。。。。?/p>

計(jì)數(shù)器的值代表著下一條需要執(zhí)行的字節(jié)碼指令,!??!
字節(jié)碼解釋器工作時(shí),
就是通過改變這個(gè)計(jì)數(shù)器的值來選取下一條需要執(zhí)行的字節(jié)碼指令,?。。?!
分支、循環(huán)、跳轉(zhuǎn)、異常處理、線程恢復(fù)基礎(chǔ)功能
都需要依賴這個(gè)計(jì)數(shù)器來完成。**

  • 為了線程切換后能恢復(fù)正確的執(zhí)行位置,
    每條線程都需要有一個(gè)獨(dú)立的程序計(jì)數(shù)器
    各條線程之間計(jì)數(shù)器互不影響,獨(dú)立存儲
    因此它是線程私有的內(nèi)存。?。。。。。?!

  • Java虛擬機(jī)規(guī)范中,
    唯一一個(gè)沒有規(guī)定任何OutOfMemoryError情況的區(qū)域。




JVM垃圾回收算法

  • 回收算法有以下四種

    • 分代收集算法(1):是當(dāng)前商業(yè)虛擬機(jī)都采用的一種算法,根據(jù)對象存活周期的不同,將Java堆劃分為新生代和老年代,并根據(jù)各個(gè)年代的特點(diǎn)采用最適當(dāng)?shù)氖占惴ā?br>
      • 新生代:大批對象死去,只有少量存活。使用『復(fù)制算法』,只需復(fù)制少量存活對象即可。

        • 復(fù)制算法(2):把可用內(nèi)存按容量劃分為大小相等的兩塊,每次只使用其中的一塊。
          當(dāng)這一塊的內(nèi)存用盡后,把還存活著的對象『復(fù)制』到另外一塊上面,
          再將這一塊內(nèi)存空間一次清理掉。
      • 老年代:對象存活率高。使用『標(biāo)記—清理算法』或者『標(biāo)記—整理算法』,只需標(biāo)記較少的回收對象即可。

        • 標(biāo)記-清除算法(3):首先『標(biāo)記』出所有需要回收的對象,然后統(tǒng)一『清除』所有被標(biāo)記的
        • 標(biāo)記-整理算法(4):首先『標(biāo)記』出所有需要回收的對象,然后進(jìn)行『整理』,使得存活的對象都向一端移動(dòng),最后直接清理掉端邊界以外的內(nèi)存。
  • 標(biāo)記-清除算法效率其實(shí)不高,
    它需要從頭到尾對內(nèi)存中的每一個(gè)對象做標(biāo)記;
    并且會(huì)產(chǎn)生大量的不連續(xù)的內(nèi)存碎片;

    如上的第四行內(nèi)存,可能兩塊藍(lán)色之間的那一塊內(nèi)存都是用不了的,
    只能用后面的三塊來分配,
    即前面出現(xiàn)了內(nèi)存空洞;

  • 復(fù)制算法的相較于 標(biāo)記-清除算法,效率是高一點(diǎn)的,
    每一次只需對二分之一的內(nèi)存進(jìn)行標(biāo)記,
    同時(shí)避免內(nèi)存空洞;
    但是浪費(fèi)了一半空間,代價(jià)大;

  • 標(biāo)記-整理算法
    避免標(biāo)記-清理導(dǎo)致的內(nèi)存碎片(及內(nèi)存空洞);
    避免復(fù)制算法的空間浪費(fèi);




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

內(nèi)存(按需)彈性分配

分配值最大值受具體設(shè)備影響;
不同配置的手機(jī),其單個(gè)APP可以使用的內(nèi)存是不同的;

比如多者有單個(gè)APP可以使用512M的內(nèi)存的,少者128M甚至更甚;



OOM場景:

OOM有時(shí)候是APP自己的原因,有時(shí)候也可能是整個(gè)系統(tǒng)的原因;

  • APP使用內(nèi)存真正不足,超限:
    比如某一個(gè)手機(jī),其單個(gè)APP 最大可以使用的內(nèi)存 是512M,
    假設(shè)有一個(gè)APP 已經(jīng)使用了510M了,這時(shí)候如果還要再申請一個(gè)3M的空間,
    這時(shí)候內(nèi)存是真正不足了,超過了最大限制,要拋出OOM內(nèi)存溢出異常;

  • 系統(tǒng)可用內(nèi)存不足:
    就是,
    即使 APP使用的內(nèi)存 沒有超過 系統(tǒng)規(guī)定的最大限制,
    但是整個(gè)系統(tǒng)的內(nèi)存已經(jīng)不夠用了,AMS回收了別的進(jìn)程 也不夠分了,
    沒辦法多分配給APP內(nèi)存了,
    這時(shí)候也會(huì)拋出OOM 內(nèi)存溢出異常;

    如某一個(gè)手機(jī),其單個(gè)APP 最大可以使用的內(nèi)存 是512M,
    一個(gè)APP只用了200M,再要申請一個(gè)幾十M的內(nèi)存時(shí),
    系統(tǒng)也拋出OOM內(nèi)存溢出異常



Dalvik 和 ART的區(qū)別(關(guān)注點(diǎn):程序運(yùn)行時(shí)、GC算法)

參考鏈接:

Android 4.4之前,Android系統(tǒng)一直都是在Dalvik 虛擬機(jī)上的,
從Android 4.4開始開始引入ART,到5.0已經(jīng)成為默認(rèn)選擇。

  • Dalvik 僅固定一種回收算法,?。。?!
    手機(jī)出廠之前已經(jīng)設(shè)定好了,運(yùn)行期間無法改變;
    另外,
    應(yīng)用程序每次運(yùn)行時(shí),?。。?!
    都需要將程序內(nèi)的代碼即使轉(zhuǎn)變?yōu)闄C(jī)器碼才能運(yùn)行,這無形中多附加了一道手續(xù),

    這就造成了耗電相對較快、占用內(nèi)存大、即使是旗艦機(jī)用久了也會(huì)卡頓嚴(yán)重的現(xiàn)象。
  • ART,Android Runtime 的簡稱。

  • 優(yōu)點(diǎn):

    • 通過在安裝應(yīng)用程序時(shí),自動(dòng)對程序進(jìn)行代碼預(yù)讀取編譯,
      讓程序直接編譯成機(jī)器語言,運(yùn)行時(shí)直接運(yùn)行 無需再做轉(zhuǎn)化,?。。?!
      免去了Dalvik模式運(yùn)行時(shí)要時(shí)時(shí)轉(zhuǎn)換代碼,
    • 實(shí)現(xiàn)高效率、省電、占用更低的系統(tǒng)內(nèi)存、手機(jī)運(yùn)行流暢。
  • 缺點(diǎn):

    • 占用略高一些的存儲空間;
    • 安裝程序時(shí)要相比普通 Dalvik 模式要長一些時(shí)間來實(shí)現(xiàn)預(yù)編譯;
  • Android5.0之后都是默認(rèn)使用ART虛擬機(jī),
    回收算法,是可以在APP運(yùn)行期間進(jìn)行選擇的,?。。?!
    可以在不同的情況下,選擇合適的垃圾回收算法;

    如果,
    APP正跑在前臺,和用戶正在交互,
    此時(shí)此景,自然響應(yīng)速度最重要!
    對于用戶來說,需要APP能夠及時(shí)響應(yīng),
    此時(shí)應(yīng)該選擇一種簡單的算法——標(biāo)記-清除算法

    如果,
    APP切到了后臺,
    則可以選擇標(biāo)記-整理算法,作為補(bǔ)充;
    (也就是說,ART 相對于 Dalvik 而言,
    具備內(nèi)存整理能力,減少內(nèi)存空洞



Low Memory Killer 機(jī)制

機(jī)制目的:保證大多數(shù)情況下,不會(huì)出現(xiàn)內(nèi)存不足的情境;

  • 針對所有進(jìn)程;

  • 當(dāng)手機(jī)內(nèi)存不足,Low Memory Killer 機(jī)制就會(huì) 針對所有進(jìn)程 進(jìn)行回收;

  • 進(jìn)程分類
    Android系統(tǒng)將進(jìn)程分為以下幾類:
    (進(jìn)程優(yōu)秀級從前往后,從高到低)
    前臺進(jìn)程,可見進(jìn)程,服務(wù)進(jìn)程,后臺進(jìn)程,空進(jìn)程;
    (Foreground進(jìn)程、Visible進(jìn)程、Service進(jìn)程、Background進(jìn)程、Empty進(jìn)程)

如果用戶按Home鍵返回桌面,那么該app成為Background進(jìn)程;
如果按Back返回,則成為Empty進(jìn)程。

  • RAM(內(nèi)存)不足時(shí),
    Low Memory Killer 會(huì)找優(yōu)先級低的進(jìn)程,優(yōu)先進(jìn)行回收,
    殺死優(yōu)先級較低的進(jìn)程,讓高優(yōu)先級進(jìn)程獲取更多內(nèi)存;
    同時(shí)還會(huì)考慮一個(gè)因素——回收收益
    即 回收 某一個(gè)進(jìn)程 能 收回 多大的內(nèi)存;

  • ActivityManagerService直接管理所有進(jìn)程的內(nèi)存資源分配。
    所有進(jìn)程要申請釋放內(nèi)存都需要通過ActivityManagerService對象。

  • 垃圾回收不定期執(zhí)行。
    當(dāng)內(nèi)存不夠時(shí)就會(huì)遍歷heap空間,把垃圾對象刪除。

  • 堆內(nèi)存,則GC的時(shí)間更






參考自
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容