1. 程式人生 > >ART執行時垃圾收集(GC)過程分析

ART執行時垃圾收集(GC)過程分析

  ART執行時與Dalvik虛擬機器一樣,都使用了Mark-Sweep演算法進行垃圾回收,因此它們的垃圾回收流程在總體上是一致的。但是ART執行時對堆的劃分更加細緻,因而在此基礎上實現了更多樣的回收策略。不同的策略有不同的回收力度,力度越大的回收策略,每次回收的記憶體就越多,並且它們都有各自的使用情景。這樣就可以使得每次執行GC時,可以最大限度地減少應用程式停頓。本文就詳細分析ART執行時的垃圾收集過程。

        ART執行時的垃圾收集收集過程如圖1所示:

圖1 ART執行時的GC執行流程

        圖1的最上面三個箭頭描述觸發GC的三種情況,左邊的流程圖描述非並行GC的執行過程,右邊的流程圖描述並行GC的執行流程,接下來我們就詳細圖中涉及到的所有細節。

        在前面ART執行時為新建立物件分配記憶體的過程分析一文中,我們提到了兩種可能會觸發GC的情況。第一種情況是沒有足夠記憶體分配請求的分存時,會呼叫Heap類的成員函式CollectGarbageInternal觸發一個原因為kGcCauseForAlloc的GC。第二種情況下分配出請求的記憶體之後,堆剩下的記憶體超過一定的閥值,就會呼叫Heap類的成員函式RequestConcurrentGC請求執行一個並行GC。

        Heap類的成員函式RequestConcurrentGC的實現如下所示:

  1. void Heap::RequestConcurrentGC(Thread* self) {  
  2.   // Make sure that we can do a concurrent GC.
  3.   Runtime* runtime = Runtime::Current();  
  4.   DCHECK(concurrent_gc_);  
  5.   if (runtime == NULL || !runtime->IsFinishedStarting() ||  
  6.       !runtime->IsConcurrentGcEnabled()) {  
  7.     return;  
  8.   }  
  9.   {  
  10.     MutexLock mu(self, *Locks::runtime_shutdown_lock_);  
  11.     if (runtime->IsShuttingDown()) {  
  12.       return;  
  13.     }  
  14.   }  
  15.   if (self->IsHandlingStackOverflow()) {  
  16.     return;  
  17.   }  
  18.   // We already have a request pending, no reason to start more until we update
  19.   // concurrent_start_bytes_.
  20.   concurrent_start_bytes_ = std::numeric_limits<size_t>::max();  
  21.   JNIEnv* env = self->GetJniEnv();  
  22.   DCHECK(WellKnownClasses::java_lang_Daemons != NULL);  
  23.   DCHECK(WellKnownClasses::java_lang_Daemons_requestGC != NULL);  
  24.   env->CallStaticVoidMethod(WellKnownClasses::java_lang_Daemons,  
  25.                             WellKnownClasses::java_lang_Daemons_requestGC);  
  26.   CHECK(!env->ExceptionCheck());  
  27. }  
       這個函式定義在檔案art/runtime/gc/heap.cc。

       只有滿足以下四個條件,Heap類的成員函式RequestConcurrentGC才會觸發一個並行GC:

       1. ART執行時已經啟動完畢。

       2. ART執行時支援並行GC。ART執行時預設是支援並行GC的,但是可以通過啟動選項-Xgc來關閉。

       3. ART執行時不是正在關閉。

       4. 當前執行緒沒有發生棧溢位。

       上述4個條件都滿足之後,Heap類的成員函式RequestConcurrentGC就將成員變數concurrent_start_bytes_的值設定為型別size_t的最大值,表示目前正有一個並行GC在等待執行,以阻止觸發另外一個並行GC。

       最後,Heap類的成員函式RequestConcurrentGC呼叫Java層的java.lang.Daemons類的靜態成員函式requestGC請求執行一次並行GC。Java層的java.lang.Daemons類在載入的時候,會啟動五個與堆或者GC相關的守護執行緒,如下所示:

  1. publicfinalclass Daemons {  
  2.     ......  
  3.     publicstaticvoid start() {  
  4.         ReferenceQueueDaemon.INSTANCE.start();  
  5.         FinalizerDaemon.INSTANCE.start();  
  6.         FinalizerWatchdogDaemon.INSTANCE.start();  
  7.         HeapTrimmerDaemon.INSTANCE.start();  
  8.         GCDaemon.INSTANCE.start();  
  9.     }  
  10.     ......  
  11. }  
       這個類定義在檔案libcore/libart/src/main/java/java/lang/Daemons.java中。

       這五個守護執行緒分別是:

       1. ReferenceQueueDaemon:引用佇列守護執行緒。我們知道,在建立引用物件的時候,可以關聯一個佇列。當被引用物件引用的物件被GC回收的時候,被引用物件就會被加入到其建立時關聯的佇列去。這個加入佇列的操作就是由ReferenceQueueDaemon守護執行緒來完成的。這樣應用程式就可以知道那些被引用物件引用的物件已經被回收了。

       2. FinalizerDaemon:析構守護執行緒。對於重寫了成員函式finalize的物件,它們被GC決定回收時,並沒有馬上被回收,而是被放入到一個佇列中,等待FinalizerDaemon守護執行緒去呼叫它們的成員函式finalize,然後再被回收。

       3. FinalizerWatchdogDaemon:析構監護守護執行緒。用來監控FinalizerDaemon執行緒的執行。一旦檢測那些重定了成員函式finalize的物件在執行成員函式finalize時超出一定的時候,那麼就會退出VM。

       4. HeapTrimmerDaemon:堆裁剪守護執行緒。用來執行裁剪堆的操作,也就是用來將那些空閒的堆記憶體歸還給系統。

       5. GCDaemon:並行GC執行緒。用來執行並行GC。

       Java層的java.lang.Daemons類的靜態成員函式requestGC被呼叫時,就會喚醒上述的並行GC執行緒,然後這個並行GC執行緒就會通過JNI呼叫Heap類的成員函式ConcurrentGC,它的實現如下所示:

  1. void Heap::ConcurrentGC(Thread* self) {  
  2.   {  
  3.     MutexLock mu(self, *Locks::runtime_shutdown_lock_);  
  4.     if (Runtime::Current()->IsShuttingDown()) {  
  5.       return;  
  6.     }  
  7.   }  
  8.   // Wait for any GCs currently running to finish.
  9.   if (WaitForConcurrentGcToComplete(self) == collector::kGcTypeNone) {  
  10.     CollectGarbageInternal(next_gc_type_, kGcCauseBackground, false);  
  11.   }  
  12. }  
        這個函式定義在檔案art/runtime/gc/heap.cc中。

        只要ART執行時當前不是處於正在關閉的狀態,那麼Heap類的成員函式ConcurrentGC就會檢查當前是否正在執行GC。如果是的話,那麼就等待它執行完成,然後再呼叫Heap類的成員函式CollectGarbageInternal觸發一個原因為kGcCauseBackground的GC。否則的話,就直接呼叫Heap類的成員函式CollectGarbageInternal觸發一個原因為kGcCauseBackground的GC。

        從這裡就可以看到,無論是觸發GC的原因是kGcCauseForAlloc,還是kGcCauseBackground,最終都是通過呼叫Heap類的成員函式CollectGarbageInternal來執行GC的。此外,還有第三種情況會觸發GC,如下所示:

  1. void Heap::CollectGarbage(bool clear_soft_references) {  
  2.   // Even if we waited for a GC we still need to do another GC since weaks allocated during the
  3.   // last GC will not have necessarily been cleared.
  4.   Thread* self = Thread::Current();  
  5.   WaitForConcurrentGcToComplete(self);  
  6.   CollectGarbageInternal(collector::kGcTypeFull, kGcCauseExplicit, clear_soft_references);  
  7. }  
        這個函式定義在檔案art/runtime/gc/heap.cc。

        當我們呼叫Java層的java.lang.System的靜態成員函式gc時,如果ART執行時支援顯式GC,那麼就它就會通過JNI呼叫Heap類的成員函式CollectGarbageInternal來觸發一個原因為kGcCauseExplicit的GC。ART執行時預設是支援顯式GC的,但是可以通過啟動選項-XX:+DisableExplicitGC來關閉。

        從上面的分析就可以看出,ART執行時在三種情況下會觸發GC,這三種情況通過三個列舉kGcCauseForAlloc、kGcCauseBackground和kGcCauseExplicitk來描述。這三人列舉的定義如下所示:

  1. // What caused the GC?
  2. enum GcCause {  
  3.   // GC triggered by a failed allocation. Thread doing allocation is blocked waiting for GC before
  4.   // retrying allocation.
  5.   kGcCauseForAlloc,  
  6.   // A background GC trying to ensure there is free memory ahead of allocations.
  7.   kGcCauseBackground,  
  8.   // An explicit System.gc() call.
  9.   kGcCauseExplicit,  
  10. };  
       這三個列舉定義在檔案art/runtime/gc/heap.h中。

       從上面的分析還可以看出,ART執行時的所有GC都是以Heap類的成員函式CollectGarbageInternal為入口,它的實現如下所示:

  1. collector::GcType Heap::CollectGarbageInternal(collector::GcType gc_type, GcCause gc_cause,  
  2.                                                bool clear_soft_references) {  
  3.   Thread* self = Thread::Current();  
  4.   ......  
  5.   // Ensure there is only one GC at a time.
  6.   bool start_collect = false;  
  7.   while (!start_collect) {  
  8.     {  
  9.       MutexLock mu(self, *gc_complete_lock_);  
  10.       if (!is_gc_running_) {  
  11.         is_gc_running_ = true;  
  12.         start_collect = true;  
  13.       }  
  14.     }  
  15.     if (!start_collect) {  
  16.       // TODO: timinglog this.
  17.       WaitForConcurrentGcToComplete(self);  
  18.       ......  
  19.     }  
  20.   }  
  21.   ......  
  22.   if (gc_type == collector::kGcTypeSticky &&  
  23.       alloc_space_->Size() < min_alloc_space_size_for_sticky_gc_) {  
  24.     gc_type = collector::kGcTypePartial;  
  25.   }  
  26.   ......  
  27.   collector::MarkSweep* collector = NULL;  
  28.   for (const auto& cur_collector : mark_sweep_collectors_) {  
  29.     if (cur_collector->IsConcurrent() == concurrent_gc_ && cur_collector->GetGcType() == gc_type) {  
  30.       collector = cur_collector;  
  31.       break;  
  32.     }  
  33.   }  
  34.   ......  
  35.   collector->clear_soft_references_ = clear_soft_references;  
  36.   collector->Run();  
  37.   ......  
  38.   {  
  39.       MutexLock mu(self, *gc_complete_lock_);  
  40.       is_gc_running_ = false;  
  41.       last_gc_type_ = gc_type;  
  42.       // Wake anyone who may have been waiting for the GC to complete.
  43.       gc_complete_cond_->Broadcast(self);  
  44. 相關推薦

    ART執行垃圾收集GC過程分析

      ART執行時與Dalvik虛擬機器一樣,都使用了Mark-Sweep演算法進行垃圾回收,因此它們的垃圾回收流程在總體上是一致的。但是ART執行時對堆的劃分更加細緻,因而在此基礎上實現了更多樣的回收策略。不同的策略有不同的回收力度,力度越大的回收策略,每次回收的記憶

    Dalvik虛擬機器垃圾收集GC過程分析

           前面我們分析了Dalvik虛擬機器堆的建立過程,以及Java物件在堆上的分配過程。這些知識都是理解Dalvik虛擬機器垃圾收集過程的基礎。垃圾收集是一個複雜的過程,它要將那些不再被引用的物件進行回收。一方面要求Dalvik虛擬機器能夠標記出哪些物件是不再被引用的

    ART執行Mark-Compact MCGC執行過程分析

           除了Semi-Space(SS)GC和Generational Semi-Space(GSS)GC,ART執行時還引入了第三種Compacting GC:Mark-Compact(MC)GC。這三種GC雖然都是Compacting GC,不過它們的實現方式卻有很

    Java垃圾回收GC機制詳解

    nbsp 引用計數 維護 png 對象 最新 新的 com 前沿 垃圾回收算法有兩種,根據不同的虛擬機策略不同 1、引用計數法 2、可達性分析法 由於我們平常使用的hotspot虛擬機用的是第二種。 那哪些是可達的呢? 這個算法的基本思想是通過一系列稱為“GC Roots”

    JVM——垃圾回收GC

    .text 永久代 lines script from nes ng- code addclass GC簡單介紹 java語言執行在java虛擬機(jvm)上。為了解決有限的空間和性能的保證這個矛盾體,jvm所具備的GC能力。能夠有效的清除不用的對象。

    JVM (四)--垃圾收集

    一、垃圾收集演算法 1、標記-清除  將存活的物件進行標記,然後清除掉未被標記的物件。 不足: 標記和清除過程中效率多不高; 會產生大量不連續的記憶體碎片,導致無法給大物件分配記憶體。 2、標記-整理 讓所有存活的物件都向一端移動,然

    執行型別資訊RTTI之遞迴列印類資訊

    深度優先 public class ClassPrint { private static void printSupers(Class<?> clazz) { String name = clazz.getCanonicalName(); System.out.p

    《自己動手寫java虛擬機器》學習筆記-----執行緒私有執行資料區go

         專案地址:https://github.com/gongxianshengjiadexiaohuihui      在執行java程式時,Java虛擬機器需要使用記憶體來存放各種各樣的資料,Java虛擬機器規範把這些記憶體的區

    Java虛擬機器 :Java垃圾回收GC機制詳解

    轉自:http://www.importnew.com/28413.html 哪些記憶體需要回收? 哪些記憶體需要回收是垃圾回收機制第一個要考慮的問題,所謂“要回收的垃圾”無非就是那些不可能再被任何途徑使用的物件。那麼如何找到這些物件? 1、引用計數法 這個演算法的實現是,給物件中新

    JVM03------垃圾收集

    一. 什麼是GC Java與C語言相比的一個優勢是,可以通過自己的JVM自動分配和回收記憶體空間。 垃圾回收機制是由垃圾收集器Garbage Collection來實現的,GC是後臺一個低優先順序的守護程序。在記憶體中低到一定限度時才會自動執行,因此垃圾回收的時間是不確定的。 為何要這樣設計:因為GC也

    Python3 垃圾回收GC

    1. 小整數物件池 整數在程式中的使用非常廣泛,Python為了優化速度,使用了小整數物件池, 避免為整數頻繁申請和銷燬記憶體空間。 Python 對小整數的定義是 [-5, 257) 這些整數物件是提前建立好的,不會被垃圾回收。在一個 Python 的程式中,所有位於這個範圍內的整數使用

    JVM 垃圾回收GC機制

    目錄 一、背景 二、 哪些記憶體需要回收? 1、引用計數演算法 2 、可達性分析演算法 三、 四種引用狀態 1、強引用 2、軟引用 3、弱引用 4、虛引用 物件死亡(被回收)前的最後一次掙扎 方法區如何判斷是否需要回收 四、垃圾收集

    C++11 | 執行型別識別RTTI

    type_info類 typeid操作符 type_index類 type_info type_info類在標頭檔案<typeinfo>中定義,代表了一個C++型別的相關資訊。一般由t

    Java應用程式執行監控方法——JVMTI的應用

    The JVM Tool Interface (JVMTI) 是一個由JVM提供的用於開發針對Java程式開發與監控工具的程式設計介面,通過JVMTI介面(Native API)可以建立代理程式(Agent)以監視和控制 Java 應用程式,包括剖析、除錯、監控

    JAVA虛擬機器之一:垃圾回收GC機制

    引言 java對於其它語言(c/c++)來說,建立一個物件使用後,不用顯式的delete/free,且能在一定程度上保證系統記憶體資源及時回收,這要功歸於java的自動垃圾回收機制(Garbage Collection,GC),但也是因為自動回收機制存在,一旦系統內洩漏或存

    Java中垃圾回收gc問題

    以下哪項陳述是正確的? A. 垃圾回收執行緒的優先順序很高,以保證不再 使用的記憶體將被及時回收 B. 垃圾收集允許程式開發者明確指定釋放 哪一個物件 C. 垃圾回收機制保證了JAVA程式不會出現 記憶體溢位 D. 進入”Dead”狀態的執行緒將被垃圾回

    執行地理資料庫*.geodatabase複製到檔案地理資料庫

     摘要 將執行時地理資料庫的內容複製到新的檔案地理資料庫中。 用法 語法 CopyRuntimeGdbToFileGdb_conversion (in_file, out_file) 引數 說明 資料型別 in_file 執行時地理資料庫將被複制到檔案地理

    再論C++之垃圾回收GC

    原理:基於引用計數(reference count)。 這種垃圾回收(gc)演算法無非都是給每一塊分配的記憶體提供引用計數,然後通過智慧指標(smart pointer)自動完成引用計數的加減,如果引用技術減少到零,就代表沒有人使用該記憶體了,這塊記憶體就可以回收了。如果可以正確的遵從這種gc的使用規則,也就

    golang的垃圾回收GC機制

    然後再到這裡 golang的垃圾回收採用的是標記-清理(Mark-and-Sweep)演算法 就是先標記出需要回收的記憶體物件快,然後在清理掉; 在這裡不介紹標記和清理的具體策略(可以參考htt

    託管堆和垃圾回收GC

    一、基礎 首先,為了深入瞭解垃圾回收(GC),我們要了解一些基礎知識: CLR:Common Language Runtime,即公共語言執行時,是一個可由多種面向CLR的程式語言使用的“執行時”,包括記憶體管理、程式集載入、安全性、異常處理和執行緒同步等核心功能。 託管程序中的兩種記憶體堆: 託管堆:C