真实的国产乱ⅩXXX66竹夫人,五月香六月婷婷激情综合,亚洲日本VA一区二区三区,亚洲精品一区二区三区麻豆

成都創(chuàng)新互聯(lián)網(wǎng)站制作重慶分公司

AndroidGC的知識點有哪些

這篇文章主要介紹了Android GC的知識點有哪些的相關(guān)知識,內(nèi)容詳細易懂,操作簡單快捷,具有一定借鑒價值,相信大家閱讀完這篇Android GC的知識點有哪些文章都會有所收獲,下面我們一起來看看吧。

創(chuàng)新互聯(lián)是專業(yè)的昌黎網(wǎng)站建設(shè)公司,昌黎接單;提供網(wǎng)站建設(shè)、做網(wǎng)站,網(wǎng)頁設(shè)計,網(wǎng)站設(shè)計,建網(wǎng)站,PHP網(wǎng)站建設(shè)等專業(yè)做網(wǎng)站服務(wù);采用PHP框架,可快速的進行昌黎網(wǎng)站開發(fā)網(wǎng)頁制作和功能擴展;專業(yè)做搜索引擎喜愛的網(wǎng)站,專業(yè)的做網(wǎng)站團隊,希望更多企業(yè)前來合作!

1. JVM內(nèi)存回收機制

1.1. 回收算法
  • 標(biāo)記回收算法(Mark and Sweep GC)
    從"GC Roots"集合開始,將內(nèi)存整個遍歷一次,保留所有可以被GC Roots直接或間接引用到的對象,而剩下的對象都當(dāng)作垃圾對待并回收,這個算法需要中斷進程內(nèi)其它組件的執(zhí)行并且可能產(chǎn)生內(nèi)存碎片。

  • 復(fù)制算法 (Copying)
    將現(xiàn)有的內(nèi)存空間分為兩快,每次只使用其中一塊,在垃圾回收時將正在使用的內(nèi)存中的存活對象復(fù)制到未被使用的內(nèi)存塊中,之后,清除正在使用的內(nèi)存塊中的所有對象,交換兩個內(nèi)存的角色,完成垃圾回收。

  • 標(biāo)記-壓縮算法 (Mark-Compact)
    先需要從根節(jié)點開始對所有可達對象做一次標(biāo)記,但之后,它并不簡單地清理未標(biāo)記的對象,而是將所有的存活對象壓縮到內(nèi)存的一端。之后,清理邊界外所有的空間。這種方法既避免了碎片的產(chǎn)生,又不需要兩塊相同的內(nèi)存空間,因此,其性價比比較高。

  • 分代
    將所有的新建對象都放入稱為年輕代的內(nèi)存區(qū)域,年輕代的特點是對象會很快回收,因此,在年輕代就選擇效率較高的復(fù)制算法。當(dāng)一個對象經(jīng)過幾次回收后依然存活,對象就會被放入稱為老生代的內(nèi)存空間。對于新生代適用于復(fù)制算法,而對于老年代則采取標(biāo)記-壓縮算法。

1.2. 復(fù)制和標(biāo)記-壓縮算法的區(qū)別

乍一看這兩個算法似乎并沒有多大的區(qū)別,都是標(biāo)記了然后挪到另外的內(nèi)存地址進行回收,那為什么不同的分代要使用不同的回收算法呢?

其實2者最大的區(qū)別在于前者是用空間換時間后者則是用時間換空間。

前者的在工作的時候是不沒有獨立的“Mark”與“Copy”階段的,而是合在一起做一個動作,就叫Scavenge(或Evacuate,或者就叫Copy)。也就是說,每發(fā)現(xiàn)一個這次收集中尚未訪問過的活對象就直接Copy到新地方,同時設(shè)置Forwarding Pointer,這樣的工作方式就需要多一份空間。

后者在工作的時候則需要分別的Mark與Compact階段,Mark階段用來發(fā)現(xiàn)并標(biāo)記所有活的對象,然后compact階段才移動對象來達到Compact的目的。如果Compact方式是Sliding Compaction,則在Mark之后就可以按順序一個個對象“滑動”到空間的某一側(cè)。因為已經(jīng)先遍歷了整個空間里的對象圖,知道所有的活對象了,所以移動的時候就可以在同一個空間內(nèi)而不需要多一份空間。

所以新生代的回收會更快一點,老年代的回收則會需要更長時間,同時壓縮階段是會暫停應(yīng)用的,所以給我們應(yīng)該盡量避免對象出現(xiàn)在老年代。

2. Dalvik虛擬機

2.1. Java堆

Java堆實際上是由一個Active堆和一個Zygote堆組成的,其中,Zygote堆用來管理Zygote進程在啟動過程中預(yù)加載和創(chuàng)建的各種對象,而Active堆是在Zygote進程Fork第一個子進程之前創(chuàng)建的。以后啟動的所有應(yīng)用程序進程是被Zygote進程Fork出來的,并都持有一個自己的Dalvik虛擬機。在創(chuàng)建應(yīng)用程序的過程中,Dalvik虛擬機采用Cow策略復(fù)制Zygote進程的地址空間。

Cow策略:一開始的時候(未復(fù)制Zygote進程的地址空間的時候),應(yīng)用程序進程和Zygote進程共享了同一個用來分配對象的堆。當(dāng)Zygote進程或者應(yīng)用程序進程對該堆進行寫操作時,內(nèi)核就會執(zhí)
行真正的拷貝操作,使得Zygote進程和應(yīng)用程序進程分別擁有自己的一份拷貝,這就是所謂的Cow。因為Copy是十分耗時的,所以必須盡量避免Copy或者盡量少的Copy。

為了實現(xiàn)這個目的,當(dāng)創(chuàng)建第一個應(yīng)用程序進程時,會將已經(jīng)使用了的那部分堆內(nèi)存劃分為一部分,還沒有使用的堆內(nèi)存劃分為另外一部分。前者就稱為Zygote堆,后者就稱為Active堆。這樣只需把zygote堆中的內(nèi)容復(fù)制給應(yīng)用程序進程就可以了。以后無論是Zygote進程,還是應(yīng)用程序進程,當(dāng)它們需要分配對象的時候,都在Active堆上進行。這樣就可以使得Zygote堆盡可能少地被執(zhí)行寫操作,因而就可以減少執(zhí)行寫時拷貝的操作。在Zygote堆里面分配的對象其實主要就是Zygote進程在啟動過程中預(yù)加載的類、資源和對象了。這意味著這些預(yù)加載的類、資源和對象可以在Zygote進程和應(yīng)用程序進程中做到長期共享。這樣既能減少拷貝操作,還能減少對內(nèi)存的需求。

2.2. 和GC有關(guān)的一些指標(biāo)

記得我們之前在優(yōu)化魅族某手機的gc卡頓問題時,發(fā)現(xiàn)他很容易觸發(fā)GC_FOR_MALLOC,這個GC類別后續(xù)會說到,是分配對象內(nèi)存不足時導(dǎo)致的??墒俏覀冇衷O(shè)置了很大的堆Size為什么還會內(nèi)存不夠呢,這里需要了解以下幾個概念:分別是Java堆的起始大?。⊿tarting Size)、最大值(Maximum Size)和增長上限值(Growth Limit)。
在啟動Dalvik虛擬機的時候,我們可以分別通過-Xms、-Xmx和-XX:HeapGrowthLimit三個選項來指定上述三個值,以上三個值分別表示表示:

  • Starting Size: Dalvik虛擬機啟動的時候,會先分配一塊初始的堆內(nèi)存給虛擬機使用。

  • Growth Limit: 是系統(tǒng)給每一個程序的最大堆上限,超過這個上限,程序就會OOM。

  • Maximum Size: 不受控情況下的最大堆內(nèi)存大小,起始就是我們在用largeheap屬性的時候,可以從系統(tǒng)獲取的最大堆大小。

同時除了上面的這個三個指標(biāo)外,還有幾個指標(biāo)也是值得我們關(guān)注的,那就是堆最小空閑值(Min Free)、堆最大空閑值(Max Free)和堆目標(biāo)利用率(Target Utilization)。假設(shè)在某一次GC之后,存活對象占用內(nèi)存的大小為LiveSize,那么這時候堆的理想大小應(yīng)該為(LiveSize / U)。但是(LiveSize / U)必須大于等于(LiveSize + MinFree)并且小于等于(LiveSize + MaxFree),每次GC后垃圾回收器都會盡量讓堆的利用率往目標(biāo)利用率靠攏。所以當(dāng)我們嘗試手動去生成一些幾百K的對象,試圖去擴大可用堆大小的時候,反而會導(dǎo)致頻繁的GC,因為這些對象的分配會導(dǎo)致GC,而GC后會讓堆內(nèi)存回到合適的比例,而我們使用的局部變量很快會被回收理論上存活對象還是那么多,我們的堆大小也會縮減回來無法達到擴充的目的。 與此同時這也是產(chǎn)生CONCURRENT GC的一個因素,后文我們會詳細講到。

2.3. GC的類型
  • GC_FOR_MALLOC: 表示是在堆上分配對象時內(nèi)存不足觸發(fā)的GC。

  • GC_CONCURRENT: 當(dāng)我們應(yīng)用程序的堆內(nèi)存達到一定量,或者可以理解為快要滿的時候,系統(tǒng)會自動觸發(fā)GC操作來釋放內(nèi)存。

  • GC_EXPLICIT: 表示是應(yīng)用程序調(diào)用System.gc、VMRuntime.gc接口或者收到SIGUSR1信號時觸發(fā)的GC。

  • GC_BEFORE_OOM: 表示是在準(zhǔn)備拋OOM異常之前進行的最后努力而觸發(fā)的GC。

實際上,GC_FOR_MALLOC、GC_CONCURRENT和GC_BEFORE_OOM三種類型的GC都是在分配對象的過程觸發(fā)的。而并發(fā)和非并發(fā)GC的區(qū)別主要在于前者在GC過程中,有條件地掛起和喚醒非GC線程,而后者在執(zhí)行GC的過程中,一直都是掛起非GC線程的。并行GC通過有條件地掛起和喚醒非GC線程,就可以使得應(yīng)用程序獲得更好的響應(yīng)性。但是同時并行GC需要多執(zhí)行一次標(biāo)記根集對象以及遞歸標(biāo)記那些在GC過程被訪問了的對象的操作,所以也需要花費更多的CPU資源。后文在ART的并發(fā)和非并發(fā)GC中我們也會著重說明下這兩者的區(qū)別。

2.4. 對象的分配和GC觸發(fā)時機
  1. 調(diào)用函數(shù)dvmHeapSourceAlloc在Java堆上分配指定大小的內(nèi)存。如果分配成功,那么就將分配得到的地址直接返回給調(diào)用者了。函數(shù)dvmHeapSourceAlloc在不改變Java堆當(dāng)前大小的前提下進行內(nèi)存分配,這是屬于輕量級的內(nèi)存分配動作。

  2. 如果上一步內(nèi)存分配失敗,這時候就需要執(zhí)行一次GC了。不過如果GC線程已經(jīng)在運行中,即gDvm.gcHeap->gcRunning的值等于true,那么就直接調(diào)用函數(shù)dvmWaitForConcurrentGcToComplete等到GC執(zhí)行完成就是了。否則的話,就需要調(diào)用函數(shù)gcForMalloc來執(zhí)行一次GC了,參數(shù)false表示不要回收軟引用對象引用的對象。

  3. GC執(zhí)行完畢后,再次調(diào)用函數(shù)dvmHeapSourceAlloc嘗試輕量級的內(nèi)存分配操作。如果分配成功,那么就將分配得到的地址直接返回給調(diào)用者了。

  4. 如果上一步內(nèi)存分配失敗,這時候就得考慮先將Java堆的當(dāng)前大小設(shè)置為Dalvik虛擬機啟動時指定的Java堆最大值,再進行內(nèi)存分配了。這是通過調(diào)用函數(shù)dvmHeapSourceAllocAndGrow來實現(xiàn)的。

  5. 如果調(diào)用函數(shù)dvmHeapSourceAllocAndGrow分配內(nèi)存成功,則直接將分配得到的地址直接返回給調(diào)用者了。

  6. 如果上一步內(nèi)存分配還是失敗,這時候就得出狠招了。再次調(diào)用函數(shù)gcForMalloc來執(zhí)行GC。參數(shù)true表示要回收軟引用對象引用的對象。

  7. GC執(zhí)行完畢,再次調(diào)用函數(shù)dvmHeapSourceAllocAndGrow進行內(nèi)存分配。這是最后一次努力了,成功與事都到此為止。
    示例圖如下:

通過這個流程可以看到,在對象的分配中會導(dǎo)致GC,第一次分配對象失敗我們會觸發(fā)GC但是不回收Soft的引用,如果再次分配還是失敗我們就會將Soft的內(nèi)存也給回收,前者觸發(fā)的GC是GC_FOR_MALLOC類型的GC,后者是GC_BEFORE_OOM類型的GC。而當(dāng)內(nèi)存分配成功后,我們會判斷當(dāng)前的內(nèi)存占用是否是達到了GC_CONCURRENT的閥值,如果達到了那么又會觸發(fā)GC_CONCURRENT。
那么這個閥值又是如何來的呢,上面我們說到的一個目標(biāo)利用率,GC后我們會記錄一個目標(biāo)值,這個值理論上需要再上述的范圍之內(nèi),如果不在我們會選取邊界值做為目標(biāo)值。虛擬機會記錄這個目標(biāo)值,當(dāng)做當(dāng)前允許總的可以分配到的內(nèi)存。同時根據(jù)目標(biāo)值減去固定值(200~500K), 當(dāng)做觸發(fā)GC_CONCURRENT事件的閾值。

2.5. 回收算法和內(nèi)存碎片

主流的大部分Davik采取的都是標(biāo)注與清理(Mark and Sweep)回收算法,也有實現(xiàn)了拷貝GC的,這一點和HotSpot是不一樣的,具體使用什么算法是在編譯期決定的,無法在運行的時候動態(tài)更換。如果在編譯dalvik虛擬機的命令中指明了"WITH_COPYING_GC"選項,則編譯"/dalvik/vm/alloc/Copying.cpp"源碼 – 此是Android中拷貝GC算法的實現(xiàn),否則編譯"/dalvik/vm/alloc/HeapSource.cpp" – 其實現(xiàn)了標(biāo)注與清理GC算法。
由于Mark and Sweep算法的缺點,容易導(dǎo)致內(nèi)存碎片,所以在這個算法下,當(dāng)我們有大量不連續(xù)小內(nèi)存的時候,再分配一個較大對象時,還是會非常容易導(dǎo)致GC,比如我們在該手機上decode圖片,具體情況如下:

所以對于Dalvik虛擬機的手機來說,我們首先要盡量避免掉頻繁生成很多臨時小變量(比如說:getView, onDraw等函數(shù)中new對象),另一個又要盡量去避免產(chǎn)生很多長生命周期的大對象。

3. ART內(nèi)存回收機制

3.1. Java堆

ART運行時內(nèi)部使用的Java堆的主要組成包括Image Space、Zygote Space、Allocation Space和Large Object Space四個Space,Image Space用來存在一些預(yù)加載的類, Zygote Space和Allocation Space與Dalvik虛擬機垃圾收集機制中的Zygote堆和Active堆的作用是一樣的,
Large Object Space就是一些離散地址的集合,用來分配一些大對象從而提高了GC的管理效率和整體性能,類似如下圖:

在下文的GC Log中,我們也能看到在ART的GC Log中包含了LOS的信息,方便我們查看大內(nèi)存的情況。

3.2. GC的類型

kGcCauseForAlloc: 當(dāng)要分配內(nèi)存的時候發(fā)現(xiàn)內(nèi)存不夠的情況下引起的GC,這種情況下的GC會Stop World.
kGcCauseBackground: 當(dāng)內(nèi)存達到一定的閥值的時候會去出發(fā)GC,這個時候是一個后臺GC,不會引起Stop World.
kGcCauseExplicit,顯示調(diào)用的時候進行的gc,如果ART打開了這個選項的情況下,在system.gc的時候會進行GC.
其他更多。

3.3. 對象的分配和GC觸發(fā)時機

由于ART下內(nèi)存分配和Dalvik下基本沒有任何區(qū)別,我直接貼圖帶過了。

3.4. 并發(fā)和非并發(fā)GC

ART在GC上不像Dalvik僅有一種回收算法,ART在不同的情況下會選擇不同的回收算法,比如Alloc內(nèi)存不夠的時候會采用非并發(fā)GC,而在Alloc后發(fā)現(xiàn)內(nèi)存達到一定閥值的時候又會觸發(fā)并發(fā)GC。同時在前后臺的情況下GC策略也不盡相同,后面我們會一一給大家說明。

  • 非并發(fā)GC
    步驟1. 調(diào)用子類實現(xiàn)的成員函數(shù)InitializePhase執(zhí)行GC初始化階段。
    步驟2. 掛起所有的ART運行時線程。
    步驟3. 調(diào)用子類實現(xiàn)的成員函數(shù)MarkingPhase執(zhí)行GC標(biāo)記階段。
    步驟4. 調(diào)用子類實現(xiàn)的成員函數(shù)ReclaimPhase執(zhí)行GC回收階段。
    步驟5. 恢復(fù)第2步掛起的ART運行時線程。
    步驟6. 調(diào)用子類實現(xiàn)的成員函數(shù)FinishPhase執(zhí)行GC結(jié)束階段。

  • 并發(fā)GC
    步驟1. 調(diào)用子類實現(xiàn)的成員函數(shù)InitializePhase執(zhí)行GC初始化階段。
    步驟2. 獲取用于訪問Java堆的鎖。
    步驟3. 調(diào)用子類實現(xiàn)的成員函數(shù)MarkingPhase執(zhí)行GC并行標(biāo)記階段。
    步驟4. 釋放用于訪問Java堆的鎖。
    步驟5. 掛起所有的ART運行時線程。
    步驟6. 調(diào)用子類實現(xiàn)的成員函數(shù)HandleDirtyObjectsPhase處理在GC并行標(biāo)記階段被修改的對象。
    步驟7. 恢復(fù)第4步掛起的ART運行時線程。
    步驟8. 重復(fù)第5到第7步,直到所有在GC并行階段被修改的對象都處理完成。
    步驟9. 獲取用于訪問Java堆的鎖。
    步驟10. 調(diào)用子類實現(xiàn)的成員函數(shù)ReclaimPhase執(zhí)行GC回收階段。
    步驟11. 釋放用于訪問Java堆的鎖。
    步驟12. 調(diào)用子類實現(xiàn)的成員函數(shù)FinishPhase執(zhí)行GC結(jié)束階段。
    所以不論是并發(fā)還是非并發(fā),都會引起Stop World的情況出現(xiàn),并發(fā)的情況下單次Stop World的時間會更短,基本區(qū)別和Dalvik類似。

    3.5. ART并發(fā)和Dalvik并發(fā)GC的差異

    首先可以通過如下2張圖來對比下。
    Dalvik GC:

    ART GC:

ART的并發(fā)GC和Dalvik的并發(fā)GC有什么區(qū)別呢,初看好像2者差不多,雖然沒有一直掛起線程,但是也會有暫停線程去執(zhí)行標(biāo)記對象的流程。通過閱讀相關(guān)文檔可以了解到ART并發(fā)GC對于Dalvik來說主要有三個優(yōu)勢點:

  1. 標(biāo)記自身
    ART在對象分配時會將新分配的對象壓入到Heap類的成員變量allocationstack描述的Allocation Stack中去,從而可以一定程度上縮減對象遍歷范圍。

  2. 預(yù)讀取
    對于標(biāo)記Allocation Stack的內(nèi)存時,會預(yù)讀取接下來要遍歷的對象,同時再取出來該對象后又會將該對象引用的其他對象壓入棧中,直至遍歷完畢。

  3. 減少Suspend時間
    在Mark階段是不會Block其他線程的,這個階段會有臟數(shù)據(jù),比如Mark發(fā)現(xiàn)不會使用的但是這個時候又被其他線程使用的數(shù)據(jù),在Mark階段也會處理一些臟數(shù)據(jù)而不是留在最后Block的時候再去處理,這樣也會減少后面Block階段對于臟數(shù)據(jù)的處理的時間。

3.6. 前后臺GC

前臺Foreground指的就是應(yīng)用程序在前臺運行時,而后臺Background就是應(yīng)用程序在后臺運行時。因此,F(xiàn)oreground GC就是應(yīng)用程序在前臺運行時執(zhí)行的GC,而Background就是應(yīng)用程序在后臺運行時執(zhí)行的GC。
應(yīng)用程序在前臺運行時,響應(yīng)性是最重要的,因此也要求執(zhí)行的GC是高效的。相反,應(yīng)用程序在后臺運行時,響應(yīng)性不是最重要的,這時候就適合用來解決堆的內(nèi)存碎片問題。因此,Mark-Sweep GC適合作為Foreground GC,而Mark-Compact GC適合作為Background GC。
由于有Compact的能力存在,碎片化在ART上可以很好的被避免,這個也是ART一個很好的能力。

3.7. ART大法好

總的來看,ART在GC上做的比Dalvik好太多了,不光是GC的效率,減少Pause時間,而且還在內(nèi)存分配上對大內(nèi)存的有單獨的分配區(qū)域,同時還能有算法在后臺做內(nèi)存整理,減少內(nèi)存碎片。對于開發(fā)者來說ART下我們基本可以避免很多類似GC導(dǎo)致的卡頓問題了。另外根據(jù)谷歌自己的數(shù)據(jù)來看,ART相對Dalvik內(nèi)存分配的效率提高了10倍,GC的效率提高了2-3倍。

4. GC Log

當(dāng)我們想要根據(jù)GC日志來追查一些GC可能造成的卡頓時,我們需要了解GC日志的組成,不同信息代表了什么含義。

4.1. Dalvik GC日志

Dalvik的日志格式基本如下:
D/dalvikvm:,,,
GC_Reason: 就是我們上文提到的,是gc_alloc還是gc_concurrent,了解到不同的原因方便我們做不同的處理。
Amount_freed: 表示系統(tǒng)通過這次GC操作釋放了多少內(nèi)存。
Heap_stats: 中會顯示當(dāng)前內(nèi)存的空閑比例以及使用情況(活動對象所占內(nèi)存 / 當(dāng)前程序總內(nèi)存)。
Pause_time: 表示這次GC操作導(dǎo)致應(yīng)用程序暫停的時間。關(guān)于這個暫停的時間,在2.3之前GC操作是不能并發(fā)進行的,也就是系統(tǒng)正在進行GC,那么應(yīng)用程序就只能阻塞住等待GC結(jié)束。而自2.3之后,GC操作改成了并發(fā)的方式進行,就是說GC的過程中不會影響到應(yīng)用程序的正常運行,但是在GC操作的開始和結(jié)束的時候會短暫阻塞一段時間,所以還有后續(xù)的一個total_time。
Total_time: 表示本次GC所花費的總時間和上面的Pause_time,也就是stop all是不一樣的,卡頓時間主要看上面的pause_time。

4.2. ART GC日志

I/art:,,,,
基本情況和Dalvik沒有什么差別,GC的Reason更多了,還多了一個OS_Space_Status.

LOS_Space_Status:Large Object Space,大對象占用的空間,這部分內(nèi)存并不是分配在堆上的,但仍屬于應(yīng)用程序內(nèi)存空間,主要用來管理 bitmap 等占內(nèi)存大的對象,避免因分配大內(nèi)存導(dǎo)致堆頻繁 GC。

關(guān)于“Android GC的知識點有哪些”這篇文章的內(nèi)容就介紹到這里,感謝各位的閱讀!相信大家對“Android GC的知識點有哪些”知識都有一定的了解,大家如果還想學(xué)習(xí)更多知識,歡迎關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道。


分享標(biāo)題:AndroidGC的知識點有哪些
URL地址:http://weahome.cn/article/ihcsss.html

其他資訊

在線咨詢

微信咨詢

電話咨詢

028-86922220(工作日)

18980820575(7×24)

提交需求

返回頂部