CoreCLR源碼探索(四) GC內存收集器的內部實現 分析篇

在這篇中我將講述GC Collector內部的實現, 這是CoreCLR中除了JIT之外最複雜部分,下面一些概念目前還沒有有公開的文檔和書籍講到。html

爲了分析這部分我花了一個多月的時間,期間也屢次向CoreCLR的開發組提問過,我有信心如下內容都是比較準確的,但若是你發現了錯誤或者有疑問的地方請指出來,
如下的內容基於CoreCLR 1.1.0的源代碼分析,之後可能會有所改變。node

由於內容過長,我分紅了兩篇,這一篇分析代碼,下一篇實際使用LLDB跟蹤GC收集垃圾的處理。linux

須要的預備知識

  • 看過BOTR中GC設計的文檔 原文 譯文
  • 看過我以前的系列文章, 碰到不明白的能夠先跳過但最少須要看一遍
  • 對c中的指針有必定了解
  • 對經常使用數據結構有必定了解, 例如鏈表
  • 對基礎c++語法有必定了解, 高級語法和STL不須要由於微軟只用了低級語法

GC的觸發

GC通常在已預留的內存不夠用或者已經分配量超過閾值時觸發,場景包括:c++

不能給分配上下文指定新的空間時

當調用try_allocate_more_space不能從segment結尾自由對象列表獲取新的空間時會觸發GC, 詳細能夠看我上一篇中分析的代碼。git

分配的數據量達到必定閾值時

閾值儲存在各個heap的dd_min_gc_size(初始值), dd_desired_allocation(動態調整值), dd_new_allocation(消耗值)中,每次給分配上下文指定空間時會減小dd_new_allocation。github

若是dd_new_allocation變爲負數或者與dd_desired_allocation的比例小於必定值則觸發GC,
觸發完GC之後會從新調整dd_new_allocation到dd_desired_allocation。web

參考new_allocation_limit, new_allocation_allowed和check_for_full_gc函數。算法

值得一提的是能夠在.Net程序中使用GC.RegisterForFullGCNotification能夠設置觸發GC須要的dd_new_allocation / dd_desired_allocation的比例(會儲存在fgn_maxgen_percent和fgn_loh_percent中), 設置一個大於0的比例可讓GC觸發的更加頻繁。windows

StressGC

容許手動設置特殊的GC觸發策略, 參考這個文檔數組

做爲例子,你能夠試着在運行程序前運行export COMPlus_GCStress=1

GCStrees會經過調用GCStress<gc_on_alloc>::MaybeTrigger(acontext)觸發,
若是你設置了COMPlus_GCStressStart環境變量,在調用MaybeTrigger必定次數後會強制觸發GC,另外還有COMPlus_GCStressStartAtJit等參數,請參考上面的文檔。

默認StressGC不會啓用。

手動觸發GC

在.Net程序中使用GC.Collect能夠觸發手動觸發GC,我相信大家都知道。

調用.Net中的GC.Collect會調用CoreCLR中的GCHeap::GarbageCollect => GarbageCollectTry => GarbageCollectGeneration。

GC的處理

如下函數大部分都在gc.cpp裏,在這個文件裏的函數我就不一一標出文件了。

GC的入口點

GC的入口點是GCHeap::GarbageCollectGeneration函數,這個函數的主要做用是中止運行引擎和調用各個gc_heap的gc_heap::garbage_collect函數

由於這一篇重點在於GC作出的處理,我將不對如何中止運行引擎和後臺GC作出詳細的解釋,但願之後能夠再寫一篇文章講述

// 第一個參數是回收垃圾的代, 例如等於1時會回收gen 0和gen 1的垃圾
// 第二個參數是觸發GC的緣由
size_t
GCHeap::GarbageCollectGeneration (unsigned int gen, gc_reason reason)
{
    dprintf (2, ("triggered a GC!"));

    // 獲取gc_heap實例,意義不大
#ifdef MULTIPLE_HEAPS
    gc_heap* hpt = gc_heap::g_heaps[0];
#else
    gc_heap* hpt = 0;
#endif //MULTIPLE_HEAPS

    // 獲取當前線程和dd數據
    Thread* current_thread = GetThread();
    BOOL cooperative_mode = TRUE;
    dynamic_data* dd = hpt->dynamic_data_of (gen);
    size_t localCount = dd_collection_count (dd);

    // 獲取GC鎖, 防止重複觸發GC
    enter_spin_lock (&gc_heap::gc_lock);
    dprintf (SPINLOCK_LOG, ("GC Egc"));
    ASSERT_HOLDING_SPIN_LOCK(&gc_heap::gc_lock);

    //don't trigger another GC if one was already in progress
    //while waiting for the lock
    {
        size_t col_count = dd_collection_count (dd);

        if (localCount != col_count)
        {
#ifdef SYNCHRONIZATION_STATS
            gc_lock_contended++;
#endif //SYNCHRONIZATION_STATS
            dprintf (SPINLOCK_LOG, ("no need GC Lgc"));
            leave_spin_lock (&gc_heap::gc_lock);

            // We don't need to release msl here 'cause this means a GC
            // has happened and would have release all msl's.
            return col_count;
         }
    }

    // 統計GC的開始時間(包括中止運行引擎使用的時間)
#ifdef COUNT_CYCLES
    int gc_start = GetCycleCount32();
#endif //COUNT_CYCLES

#ifdef TRACE_GC
#ifdef COUNT_CYCLES
    AllocDuration += GetCycleCount32() - AllocStart;
#else
    AllocDuration += clock() - AllocStart;
#endif //COUNT_CYCLES
#endif //TRACE_GC

    // 設置觸發GC的緣由
    gc_heap::g_low_memory_status = (reason == reason_lowmemory) || 
                                   (reason == reason_lowmemory_blocking) ||
                                   g_bLowMemoryFromHost;

    if (g_bLowMemoryFromHost)
        reason = reason_lowmemory_host;

    gc_trigger_reason = reason;

    // 重設GC結束的事件
    // 如下說的"事件"的做用和"信號量", .Net中的"Monitor"同樣
#ifdef MULTIPLE_HEAPS
    for (int i = 0; i < gc_heap::n_heaps; i++)
    {
        gc_heap::g_heaps[i]->reset_gc_done();
    }
#else
    gc_heap::reset_gc_done();
#endif //MULTIPLE_HEAPS

    // 標記gc已開始, 全局靜態變量
    gc_heap::gc_started = TRUE;

    // 中止運行引擎
    {
        init_sync_log_stats();

#ifndef MULTIPLE_HEAPS
        // 讓當前線程進入preemptive模式
        // 最終會調用Thread::EnablePreemptiveGC
        // 設置線程的m_fPreemptiveGCDisabled等於0
        cooperative_mode = gc_heap::enable_preemptive (current_thread);

        dprintf (2, ("Suspending EE"));
        BEGIN_TIMING(suspend_ee_during_log);
        
        // 中止運行引擎,這裏我只作簡單解釋
        // - 調用ThreadSuspend::SuspendEE
        //   - 調用LockThreadStore鎖住線程集合直到RestartEE
        //   - 設置GCHeap中全局事件WaitForGCEvent
        //   - 調用ThreadStore::TrapReturingThreads
        //     - 設置全局變量g_TrapReturningThreads,jit會生成檢查這個全局變量的代碼
        //   - 調用SuspendRuntime, 中止除了當前線程之外的線程,若是線程在cooperative模式則劫持並中止,若是線程在preemptive模式則阻止進入cooperative模式
        GCToEEInterface::SuspendEE(GCToEEInterface::SUSPEND_FOR_GC);
        END_TIMING(suspend_ee_during_log);
        // 再次判斷是否應該執行gc
        // 目前若是設置了NoGCRegion(gc_heap::settings.pause_mode == pause_no_gc)則會進一步檢查
        // https://msdn.microsoft.com/en-us/library/system.runtime.gclatencymode(v=vs.110).aspx
        gc_heap::proceed_with_gc_p = gc_heap::should_proceed_with_gc();
        // 設置當前線程離開preemptive模式
        gc_heap::disable_preemptive (current_thread, cooperative_mode);
        if (gc_heap::proceed_with_gc_p)
            pGenGCHeap->settings.init_mechanisms();
        else
            gc_heap::update_collection_counts_for_no_gc();

#endif //!MULTIPLE_HEAPS
    }

// MAP_EVENT_MONITORS(EE_MONITOR_GARBAGE_COLLECTIONS, NotifyEvent(EE_EVENT_TYPE_GC_STARTED, 0));

    // 統計GC的開始時間
#ifdef TRACE_GC
#ifdef COUNT_CYCLES
    unsigned start;
    unsigned finish;
    start = GetCycleCount32();
#else
    clock_t start;
    clock_t finish;
    start = clock();
#endif //COUNT_CYCLES
    PromotedObjectCount = 0;
#endif //TRACE_GC

    // 當前收集代的序號
    // 後面看到condemned generation時都表示"當前收集代"
    unsigned int condemned_generation_number = gen;

    // We want to get a stack from the user thread that triggered the GC
    // instead of on the GC thread which is the case for Server GC.
    // But we are doing it for Workstation GC as well to be uniform.
    FireEtwGCTriggered((int) reason, GetClrInstanceId());

    // 進入GC處理
    // 若是有多個heap(服務器GC),可使用各個heap的線程並行處理
    // 若是隻有一個heap(工做站GC),直接在當前線程處理
#ifdef MULTIPLE_HEAPS
    GcCondemnedGeneration = condemned_generation_number;

    // 當前線程進入preemptive模式
    cooperative_mode = gc_heap::enable_preemptive (current_thread);

    BEGIN_TIMING(gc_during_log);
    // gc_heap::gc_thread_function在收到這個信號之後會進入GC處理
    // 在裏面也會判斷proceed_with_gc_p
    gc_heap::ee_suspend_event.Set();
    // 等待全部線程處理完畢
    gc_heap::wait_for_gc_done();
    END_TIMING(gc_during_log);

    // 當前線程離開preemptive模式
    gc_heap::disable_preemptive (current_thread, cooperative_mode);

    condemned_generation_number = GcCondemnedGeneration;
#else
    // 在當前線程中進入GC處理
    if (gc_heap::proceed_with_gc_p)
    {
        BEGIN_TIMING(gc_during_log);
        pGenGCHeap->garbage_collect (condemned_generation_number);
        END_TIMING(gc_during_log);
    }
#endif //MULTIPLE_HEAPS

    // 統計GC的結束時間
#ifdef TRACE_GC
#ifdef COUNT_CYCLES
    finish = GetCycleCount32();
#else
    finish = clock();
#endif //COUNT_CYCLES
    GcDuration += finish - start;
    dprintf (3,
             ("<GC# %d> Condemned: %d, Duration: %d, total: %d Alloc Avg: %d, Small Objects:%d Large Objects:%d",
              VolatileLoad(&pGenGCHeap->settings.gc_index), condemned_generation_number,
              finish - start, GcDuration,
              AllocCount ? (AllocDuration / AllocCount) : 0,
              AllocSmallCount, AllocBigCount));
    AllocCount = 0;
    AllocDuration = 0;
#endif // TRACE_GC

#ifdef BACKGROUND_GC
    // We are deciding whether we should fire the alloc wait end event here
    // because in begin_foreground we could be calling end_foreground 
    // if we need to retry.
    if (gc_heap::alloc_wait_event_p)
    {
        hpt->fire_alloc_wait_event_end (awr_fgc_wait_for_bgc);
        gc_heap::alloc_wait_event_p = FALSE;
    }
#endif //BACKGROUND_GC

    // 重啓運行引擎
#ifndef MULTIPLE_HEAPS
#ifdef BACKGROUND_GC
    if (!gc_heap::dont_restart_ee_p)
    {
#endif //BACKGROUND_GC
        BEGIN_TIMING(restart_ee_during_log);
        // 重啓運行引擎,這裏我只作簡單解釋
        // - 調用SetGCDone
        // - 調用ResumeRuntime
        // - 調用UnlockThreadStore
        GCToEEInterface::RestartEE(TRUE);
        END_TIMING(restart_ee_during_log);
#ifdef BACKGROUND_GC
    }
#endif //BACKGROUND_GC
#endif //!MULTIPLE_HEAPS

#ifdef COUNT_CYCLES
    printf ("GC: %d Time: %d\n", GcCondemnedGeneration,
            GetCycleCount32() - gc_start);
#endif //COUNT_CYCLES

    // 設置gc_done_event事件和釋放gc鎖
    // 若是有多個heap, 這裏的處理會在gc_thread_function中完成
#ifndef MULTIPLE_HEAPS
    process_sync_log_stats();
    gc_heap::gc_started = FALSE;
    gc_heap::set_gc_done();
    dprintf (SPINLOCK_LOG, ("GC Lgc"));
    leave_spin_lock (&gc_heap::gc_lock);    
#endif //!MULTIPLE_HEAPS

#ifdef FEATURE_PREMORTEM_FINALIZATION
    if ((!pGenGCHeap->settings.concurrent && pGenGCHeap->settings.found_finalizers) || 
        FinalizerThread::HaveExtraWorkForFinalizer())
    {
        FinalizerThread::EnableFinalization();
    }
#endif // FEATURE_PREMORTEM_FINALIZATION

    return dd_collection_count (dd);
}

如下是gc_heap::garbage_collect函數,這個函數也是GC的入口點函數,
主要做用是針對gc_heap作gc開始前和結束後的清理工做,例如重設各個線程的分配上下文和修改gc參數

// 第一個參數是回收垃圾的代
int gc_heap::garbage_collect (int n)
{
    // 枚舉線程
    // - 統計目前用的分配上下文數量
    // - 在分配上下文的alloc_ptr和limit之間建立free object
    // - 設置全部分配上下文的alloc_ptr和limit到0
    //reset the number of alloc contexts
    alloc_contexts_used = 0;
    fix_allocation_contexts (TRUE);

    // 清理在gen 0範圍的brick table
    // brick table將在下面解釋
#ifdef MULTIPLE_HEAPS
    clear_gen0_bricks();
#endif //MULTIPLE_HEAPS

    // 若是開始了NoGCRegion,而且disallowFullBlockingGC等於true,則跳過此次GC
    // https://msdn.microsoft.com/en-us/library/dn906204(v=vs.110).aspx
    if ((settings.pause_mode == pause_no_gc) && current_no_gc_region_info.minimal_gc_p)
    {
#ifdef MULTIPLE_HEAPS
        gc_t_join.join(this, gc_join_minimal_gc);
        if (gc_t_join.joined())
        {
#endif //MULTIPLE_HEAPS

#ifdef MULTIPLE_HEAPS
            // this is serialized because we need to get a segment
            for (int i = 0; i < n_heaps; i++)
            {
                if (!(g_heaps[i]->expand_soh_with_minimal_gc()))
                    current_no_gc_region_info.start_status = start_no_gc_no_memory;
            }
#else
            if (!expand_soh_with_minimal_gc())
                current_no_gc_region_info.start_status = start_no_gc_no_memory;
#endif //MULTIPLE_HEAPS

            update_collection_counts_for_no_gc();

#ifdef MULTIPLE_HEAPS
            gc_t_join.restart();
        }
#endif //MULTIPLE_HEAPS

        goto done;
    }

    // 清空gc_data_per_heap和fgm_result
    init_records();
    memset (&fgm_result, 0, sizeof (fgm_result));

    // 設置收集理由到settings成員中
    // settings成員的類型是gc_mechanisms, 裏面的值已在前面初始化過,將會貫穿整個gc過程使用
    settings.reason = gc_trigger_reason;
    verify_pinned_queue_p = FALSE;

#if defined(ENABLE_PERF_COUNTERS) || defined(FEATURE_EVENT_TRACE)
        num_pinned_objects = 0;
#endif //ENABLE_PERF_COUNTERS || FEATURE_EVENT_TRACE

#ifdef STRESS_HEAP
    if (settings.reason == reason_gcstress)
    {
        settings.reason = reason_induced;
        settings.stress_induced = TRUE;
    }
#endif // STRESS_HEAP

#ifdef MULTIPLE_HEAPS
    // 根據環境從新決定應該收集的代
    // 這裏的處理比較雜,大概包括瞭如下的處理
    // - 備份dd_new_allocation到dd_gc_new_allocation
    // - 必要時修改收集的代, 例如最大代的閾值用完或者須要低延遲的時候
    // - 必要時設置settings.promotion = true (啓用對象升代, 例如代0對象gc後變代1)
    //   - 算法是 經過卡片標記的對象 / 經過卡片掃描的對象 < 30% 則啓用對象升代(dt_low_card_table_efficiency_p)
    //   - 這個比例儲存在`generation_skip_ratio`中
    //   - Card Table將在下面解釋,意義是若是前一代的對象不夠多則須要把後一代的對象升代
    //align all heaps on the max generation to condemn
    dprintf (3, ("Joining for max generation to condemn"));
    condemned_generation_num = generation_to_condemn (n, 
                                                      &blocking_collection, 
                                                      &elevation_requested, 
                                                      FALSE);
    gc_t_join.join(this, gc_join_generation_determined);
    if (gc_t_join.joined())
#endif //MULTIPLE_HEAPS
    {
    // 判斷是否要打印更多的除錯信息,除錯用
#ifdef TRACE_GC
        int gc_count = (int)dd_collection_count (dynamic_data_of (0));
        if (gc_count >= g_pConfig->GetGCtraceStart())
            trace_gc = 1;
        if (gc_count >=  g_pConfig->GetGCtraceEnd())
            trace_gc = 0;
#endif //TRACE_GC

    // 複製(合併)各個heap的card table和brick table到全局
#ifdef MULTIPLE_HEAPS
#if !defined(SEG_MAPPING_TABLE) && !defined(FEATURE_BASICFREEZE)
        // 釋放已刪除的segment索引的節點
        //delete old slots from the segment table
        seg_table->delete_old_slots();
#endif //!SEG_MAPPING_TABLE && !FEATURE_BASICFREEZE
        for (int i = 0; i < n_heaps; i++)
        {
            //copy the card and brick tables
            if (g_card_table != g_heaps[i]->card_table)
            {
                g_heaps[i]->copy_brick_card_table();
            }

            g_heaps[i]->rearrange_large_heap_segments();
            if (!recursive_gc_sync::background_running_p())
            {
                g_heaps[i]->rearrange_small_heap_segments();
            }
        }
#else //MULTIPLE_HEAPS
#ifdef BACKGROUND_GC
            //delete old slots from the segment table
#if !defined(SEG_MAPPING_TABLE) && !defined(FEATURE_BASICFREEZE)
            // 釋放已刪除的segment索引的節點
            seg_table->delete_old_slots();
#endif //!SEG_MAPPING_TABLE && !FEATURE_BASICFREEZE
            // 刪除空segment
            rearrange_large_heap_segments();
            if (!recursive_gc_sync::background_running_p())
            {
                rearrange_small_heap_segments();
            }
#endif //BACKGROUND_GC
        // check for card table growth
        if (g_card_table != card_table)
            copy_brick_card_table();

#endif //MULTIPLE_HEAPS

    // 合併各個heap的elevation_requested和blocking_collection選項
    BOOL should_evaluate_elevation = FALSE;
    BOOL should_do_blocking_collection = FALSE;

#ifdef MULTIPLE_HEAPS
    int gen_max = condemned_generation_num;
    for (int i = 0; i < n_heaps; i++)
    {
        if (gen_max < g_heaps[i]->condemned_generation_num)
            gen_max = g_heaps[i]->condemned_generation_num;
        if ((!should_evaluate_elevation) && (g_heaps[i]->elevation_requested))
            should_evaluate_elevation = TRUE;
        if ((!should_do_blocking_collection) && (g_heaps[i]->blocking_collection))
            should_do_blocking_collection = TRUE;
    }

    settings.condemned_generation = gen_max;
//logically continues after GC_PROFILING.
#else //MULTIPLE_HEAPS
    // 單gc_heap(工做站GC)時的處理
    // 根據環境從新決定應該收集的代,解釋看上面
    settings.condemned_generation = generation_to_condemn (n, 
                                                           &blocking_collection, 
                                                           &elevation_requested, 
                                                           FALSE);
    should_evaluate_elevation = elevation_requested;
    should_do_blocking_collection = blocking_collection;
#endif //MULTIPLE_HEAPS
    settings.condemned_generation = joined_generation_to_condemn (
                                        should_evaluate_elevation, 
                                        settings.condemned_generation,
                                        &should_do_blocking_collection
                                        STRESS_HEAP_ARG(n)
                                        );

    STRESS_LOG1(LF_GCROOTS|LF_GC|LF_GCALLOC, LL_INFO10, 
            "condemned generation num: %d\n", settings.condemned_generation);

    record_gcs_during_no_gc();

    // 若是收集代大於1(目前只有2,也就是full gc)則啓用對象升代
    if (settings.condemned_generation > 1)
        settings.promotion = TRUE;

#ifdef HEAP_ANALYZE
    // At this point we've decided what generation is condemned
    // See if we've been requested to analyze survivors after the mark phase
    if (AnalyzeSurvivorsRequested(settings.condemned_generation))
    {
        heap_analyze_enabled = TRUE;
    }
#endif // HEAP_ANALYZE

    // 統計GC性能的處理,這裏不分析
#ifdef GC_PROFILING

        // If we're tracking GCs, then we need to walk the first generation
        // before collection to track how many items of each class has been
        // allocated.
        UpdateGenerationBounds();
        GarbageCollectionStartedCallback(settings.condemned_generation, settings.reason == reason_induced);
        {
            BEGIN_PIN_PROFILER(CORProfilerTrackGC());
            size_t profiling_context = 0;

#ifdef MULTIPLE_HEAPS
            int hn = 0;
            for (hn = 0; hn < gc_heap::n_heaps; hn++)
            {
                gc_heap* hp = gc_heap::g_heaps [hn];

                // When we're walking objects allocated by class, then we don't want to walk the large
                // object heap because then it would count things that may have been around for a while.
                hp->walk_heap (&AllocByClassHelper, (void *)&profiling_context, 0, FALSE);
            }
#else
            // When we're walking objects allocated by class, then we don't want to walk the large
            // object heap because then it would count things that may have been around for a while.
            gc_heap::walk_heap (&AllocByClassHelper, (void *)&profiling_context, 0, FALSE);
#endif //MULTIPLE_HEAPS

            // Notify that we've reached the end of the Gen 0 scan
            g_profControlBlock.pProfInterface->EndAllocByClass(&profiling_context);
            END_PIN_PROFILER();
        }

#endif // GC_PROFILING

    // 後臺GC的處理,這裏不分析
#ifdef BACKGROUND_GC
        if ((settings.condemned_generation == max_generation) &&
            (recursive_gc_sync::background_running_p()))
        {
            //TODO BACKGROUND_GC If we just wait for the end of gc, it won't woork
            // because we have to collect 0 and 1 properly
            // in particular, the allocation contexts are gone.
            // For now, it is simpler to collect max_generation-1
            settings.condemned_generation = max_generation - 1;
            dprintf (GTC_LOG, ("bgc - 1 instead of 2"));
        }

        if ((settings.condemned_generation == max_generation) &&
            (should_do_blocking_collection == FALSE) &&
            gc_can_use_concurrent &&
            !temp_disable_concurrent_p &&                 
            ((settings.pause_mode == pause_interactive) || (settings.pause_mode == pause_sustained_low_latency)))
        {
            keep_bgc_threads_p = TRUE;
            c_write (settings.concurrent,  TRUE);
        }
#endif //BACKGROUND_GC

        // 當前gc的標識序號(會在gc1 => update_collection_counts函數裏面更新)
        settings.gc_index = (uint32_t)dd_collection_count (dynamic_data_of (0)) + 1;

        // 通知運行引擎GC開始工做
        // 這裏會作出一些處理例如釋放JIT中已刪除的HostCodeHeap的內存
        // Call the EE for start of GC work
        // just one thread for MP GC
        GCToEEInterface::GcStartWork (settings.condemned_generation,
                                 max_generation);            

        // TODO: we could fire an ETW event to say this GC as a concurrent GC but later on due to not being able to
        // create threads or whatever, this could be a non concurrent GC. Maybe for concurrent GC we should fire
        // it in do_background_gc and if it failed to be a CGC we fire it in gc1... in other words, this should be
        // fired in gc1.

        // 更新一些統計用計數器和數據
        do_pre_gc();

        // 繼續(喚醒)後臺GC線程
#ifdef MULTIPLE_HEAPS
        gc_start_event.Reset();
        //start all threads on the roots.
        dprintf(3, ("Starting all gc threads for gc"));
        gc_t_join.restart();
#endif //MULTIPLE_HEAPS
    }

    // 更新統計數據
    {
        int gen_num_for_data = max_generation + 1;
        for (int i = 0; i <= gen_num_for_data; i++)
        {
            gc_data_per_heap.gen_data[i].size_before = generation_size (i);
            generation* gen = generation_of (i);
            gc_data_per_heap.gen_data[i].free_list_space_before = generation_free_list_space (gen);
            gc_data_per_heap.gen_data[i].free_obj_space_before = generation_free_obj_space (gen);
        }
    }
    // 打印出錯信息
    descr_generations (TRUE);
//    descr_card_table();

    // 若是不使用Write Barrier而是Write Watch時則須要更新Card Table
    // 默認windows和linux編譯的CoreCLR都會使用Write Barrier
    // Write Barrier和Card Table將在下面解釋
#ifdef NO_WRITE_BARRIER
    fix_card_table();
#endif //NO_WRITE_BARRIER

    // 檢查gc_heap的狀態,除錯用
#ifdef VERIFY_HEAP
    if ((g_pConfig->GetHeapVerifyLevel() & EEConfig::HEAPVERIFY_GC) &&
       !(g_pConfig->GetHeapVerifyLevel() & EEConfig::HEAPVERIFY_POST_GC_ONLY))
    {
        verify_heap (TRUE);
    }
    if (g_pConfig->GetHeapVerifyLevel() & EEConfig::HEAPVERIFY_BARRIERCHECK)
        checkGCWriteBarrier();

#endif // VERIFY_HEAP

    // 調用GC的主函數`gc1`
    // 後臺GC的處理我在這一篇中將不會解釋,但願之後能夠專門寫一篇解釋後臺GC
#ifdef BACKGROUND_GC
    if (settings.concurrent)
    {
        // We need to save the settings because we'll need to restore it after each FGC.
        assert (settings.condemned_generation == max_generation);
        settings.compaction = FALSE;
        saved_bgc_settings = settings;

#ifdef MULTIPLE_HEAPS
        if (heap_number == 0)
        {
            for (int i = 0; i < n_heaps; i++)
            {
                prepare_bgc_thread (g_heaps[i]);
            }
            dprintf (2, ("setting bgc_threads_sync_event"));
            bgc_threads_sync_event.Set();
        }
        else
        {
            bgc_threads_sync_event.Wait(INFINITE, FALSE);
            dprintf (2, ("bgc_threads_sync_event is signalled"));
        }
#else
        prepare_bgc_thread(0);
#endif //MULTIPLE_HEAPS

#ifdef MULTIPLE_HEAPS
        gc_t_join.join(this, gc_join_start_bgc);
        if (gc_t_join.joined())
#endif //MULTIPLE_HEAPS
        {
            do_concurrent_p = TRUE;
            do_ephemeral_gc_p = FALSE;
#ifdef MULTIPLE_HEAPS
            dprintf(2, ("Joined to perform a background GC"));

            for (int i = 0; i < n_heaps; i++)
            {
                gc_heap* hp = g_heaps[i];
                if (!(hp->bgc_thread) || !hp->commit_mark_array_bgc_init (hp->mark_array))
                {
                    do_concurrent_p = FALSE;
                    break;
                }
                else
                {
                    hp->background_saved_lowest_address = hp->lowest_address;
                    hp->background_saved_highest_address = hp->highest_address;
                }
            }
#else
            do_concurrent_p = (!!bgc_thread && commit_mark_array_bgc_init (mark_array));
            if (do_concurrent_p)
            {
                background_saved_lowest_address = lowest_address;
                background_saved_highest_address = highest_address;
            }
#endif //MULTIPLE_HEAPS

            if (do_concurrent_p)
            {
#ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
                SoftwareWriteWatch::EnableForGCHeap();
#endif //FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP

#ifdef MULTIPLE_HEAPS
                for (int i = 0; i < n_heaps; i++)
                    g_heaps[i]->current_bgc_state = bgc_initialized;
#else
                current_bgc_state = bgc_initialized;
#endif //MULTIPLE_HEAPS

                int gen = check_for_ephemeral_alloc();
                // always do a gen1 GC before we start BGC. 
                // This is temporary for testing purpose.
                //int gen = max_generation - 1;
                dont_restart_ee_p = TRUE;
                if (gen == -1)
                {
                    // If we decide to not do a GC before the BGC we need to 
                    // restore the gen0 alloc context.
#ifdef MULTIPLE_HEAPS
                    for (int i = 0; i < n_heaps; i++)
                    {
                        generation_allocation_pointer (g_heaps[i]->generation_of (0)) =  0;
                        generation_allocation_limit (g_heaps[i]->generation_of (0)) = 0;
                    }
#else
                    generation_allocation_pointer (youngest_generation) =  0;
                    generation_allocation_limit (youngest_generation) = 0;
#endif //MULTIPLE_HEAPS
                }
                else
                {
                    do_ephemeral_gc_p = TRUE;

                    settings.init_mechanisms();
                    settings.condemned_generation = gen;
                    settings.gc_index = (size_t)dd_collection_count (dynamic_data_of (0)) + 2;
                    do_pre_gc();

                    // TODO BACKGROUND_GC need to add the profiling stuff here.
                    dprintf (GTC_LOG, ("doing gen%d before doing a bgc", gen));
                }

                //clear the cards so they don't bleed in gen 1 during collection
                // shouldn't this always be done at the beginning of any GC?
                //clear_card_for_addresses (
                //    generation_allocation_start (generation_of (0)),
                //    heap_segment_allocated (ephemeral_heap_segment));

                if (!do_ephemeral_gc_p)
                {
                    do_background_gc();
                }
            }
            else
            {
                settings.compaction = TRUE;
                c_write (settings.concurrent, FALSE);
            }

#ifdef MULTIPLE_HEAPS
            gc_t_join.restart();
#endif //MULTIPLE_HEAPS
        }

        if (do_concurrent_p)
        {
            // At this point we are sure we'll be starting a BGC, so save its per heap data here.
            // global data is only calculated at the end of the GC so we don't need to worry about
            // FGCs overwriting it.
            memset (&bgc_data_per_heap, 0, sizeof (bgc_data_per_heap));
            memcpy (&bgc_data_per_heap, &gc_data_per_heap, sizeof(gc_data_per_heap));

            if (do_ephemeral_gc_p)
            {
                dprintf (2, ("GC threads running, doing gen%d GC", settings.condemned_generation));

                gen_to_condemn_reasons.init();
                gen_to_condemn_reasons.set_condition (gen_before_bgc);
                gc_data_per_heap.gen_to_condemn_reasons.init (&gen_to_condemn_reasons);
                gc1();
#ifdef MULTIPLE_HEAPS
                gc_t_join.join(this, gc_join_bgc_after_ephemeral);
                if (gc_t_join.joined())
#endif //MULTIPLE_HEAPS
                {
#ifdef MULTIPLE_HEAPS
                    do_post_gc();
#endif //MULTIPLE_HEAPS
                    settings = saved_bgc_settings;
                    assert (settings.concurrent);

                    do_background_gc();

#ifdef MULTIPLE_HEAPS
                    gc_t_join.restart();
#endif //MULTIPLE_HEAPS
                }
            }
        }
        else
        {
            dprintf (2, ("couldn't create BGC threads, reverting to doing a blocking GC"));
            gc1();
        }
    }
    else
#endif //BACKGROUND_GC
    {
        gc1();
    }
#ifndef MULTIPLE_HEAPS
    allocation_running_time = (size_t)GCToOSInterface::GetLowPrecisionTimeStamp();
    allocation_running_amount = dd_new_allocation (dynamic_data_of (0));
    fgn_last_alloc = dd_new_allocation (dynamic_data_of (0));
#endif //MULTIPLE_HEAPS

done:
    if (settings.pause_mode == pause_no_gc)
        allocate_for_no_gc_after_gc();

    int gn = settings.condemned_generation;
    return gn;
}

GC的主函數

GC的主函數是gc1,包含了GC中最關鍵的處理,也是這一篇中須要重點講解的部分。

gc1中的整體流程在BOTR文檔已經有初步的介紹:

  • 首先是mark phase,標記存活的對象
  • 而後是plan phase,決定要壓縮仍是要清掃
  • 若是要壓縮則進入relocate phasecompact phase
  • 若是要清掃則進入sweep phase

在看具體的代碼以前讓咱們一塊兒複習以前講到的Object的結構

GC使用其中的2個bit來保存標記(marked)固定(pinned)

  • 標記(marked)表示對象是存活的,不該該被收集,儲存在MethodTable指針 & 1中
  • 固定(pinned)表示對象不能被移動(壓縮時不要移動這個對象), 儲存在對象頭 & 0x20000000中
    這兩個bit會在mark_phase中被標記,在plan_phase中被清除,不會殘留到GC結束後

再複習堆段(heap segment)的結構

一個gc_heap中有兩個segment鏈表,一個是小對象(gen 0~gen 2)用的鏈表,一個是大對象(gen 3)用的鏈表,
其中鏈表的最後一個節點是ephemeral heap segment,只用來保存gen 0和gen 1的對象,各個代都有一個開始地址,在開始地址以後的對象屬於這個代或更年輕的代。

gc_heap::gc1函數的代碼以下

//internal part of gc used by the serial and concurrent version
void gc_heap::gc1()
{
#ifdef BACKGROUND_GC
    assert (settings.concurrent == (uint32_t)(bgc_thread_id.IsCurrentThread()));
#endif //BACKGROUND_GC

    // 開始統計各個階段的時間,這些是全局變量
#ifdef TIME_GC
    mark_time = plan_time = reloc_time = compact_time = sweep_time = 0;
#endif //TIME_GC

    // 驗證小對象的segment列表(gen0~2的segment),除錯用
    verify_soh_segment_list();

    int n = settings.condemned_generation;

    // gc的標識序號+1
    update_collection_counts ();

    // 調用mark_phase和plan_phase(包括relocate, compact, sweep)
    // 後臺GC這一篇不解釋,請跳到#endif //BACKGROUND_GC
#ifdef BACKGROUND_GC
    bgc_alloc_lock->check();
#endif //BACKGROUND_GC

    // 打印除錯信息
    free_list_info (max_generation, "beginning");

    // 設置當前收集代
    vm_heap->GcCondemnedGeneration = settings.condemned_generation;

    assert (g_card_table == card_table);

    {
        // 設置收集範圍
        // 若是收集gen 2則從最小的地址一直到最大的地址
        // 不然從收集代的開始地址一直到短暫的堆段(ephemeral heap segment)的預留地址
        if (n == max_generation)
        {
            gc_low = lowest_address;
            gc_high = highest_address;
        }
        else
        {
            gc_low = generation_allocation_start (generation_of (n));
            gc_high = heap_segment_reserved (ephemeral_heap_segment);
        }   
#ifdef BACKGROUND_GC
        if (settings.concurrent)
        {
#ifdef TRACE_GC
            time_bgc_last = GetHighPrecisionTimeStamp();
#endif //TRACE_GC

            fire_bgc_event (BGCBegin);

            concurrent_print_time_delta ("BGC");

//#ifdef WRITE_WATCH
            //reset_write_watch (FALSE);
//#endif //WRITE_WATCH

            concurrent_print_time_delta ("RW");
            background_mark_phase();
            free_list_info (max_generation, "after mark phase");
            
            background_sweep();
            free_list_info (max_generation, "after sweep phase");
        }
        else
#endif //BACKGROUND_GC
        {
            // 調用mark_phase標記存活的對象
            // 請看下面的詳解
            mark_phase (n, FALSE);
            // 設置對象結構有可能不合法,由於plan_phase中可能會對對象作出臨時性的破壞
            GCScan::GcRuntimeStructuresValid (FALSE);
            // 調用plan_phase計劃是否要壓縮仍是清掃
            // 這個函數內部會完成壓縮或者清掃,請看下面的詳解
            plan_phase (n);
            // 從新設置對象結構合法
            GCScan::GcRuntimeStructuresValid (TRUE);
        }
    }

    // 記錄gc結束時間
    size_t end_gc_time = GetHighPrecisionTimeStamp();
//    printf ("generation: %d, elapsed time: %Id\n", n,  end_gc_time - dd_time_clock (dynamic_data_of (0)));

    // 調整generation_pinned_allocated(固定對象的大小)和generation_allocation_size(分配的大小)
    //adjust the allocation size from the pinned quantities. 
    for (int gen_number = 0; gen_number <= min (max_generation,n+1); gen_number++)
    {
        generation* gn = generation_of (gen_number);
        if (settings.compactin)
        {
            generation_pinned_allocated (gn) += generation_pinned_allocation_compact_size (gn);
            generation_allocation_size (generation_of (gen_number)) += generation_pinned_allocation_compact_size (gn);
        }
        else
        {
            generation_pinned_allocated (gn) += generation_pinned_allocation_sweep_size (gn);
            generation_allocation_size (generation_of (gen_number)) += generation_pinned_allocation_sweep_size (gn);
        }
        generation_pinned_allocation_sweep_size (gn) = 0;
        generation_pinned_allocation_compact_size (gn) = 0;
    }

    // 更新gc_data_per_heap, 和打印除錯信息
#ifdef BACKGROUND_GC
    if (settings.concurrent)
    {
        dynamic_data* dd = dynamic_data_of (n);
        dd_gc_elapsed_time (dd) = end_gc_time - dd_time_clock (dd);

        free_list_info (max_generation, "after computing new dynamic data");

        gc_history_per_heap* current_gc_data_per_heap = get_gc_data_per_heap();

        for (int gen_number = 0; gen_number < max_generation; gen_number++)
        {
            dprintf (2, ("end of BGC: gen%d new_alloc: %Id", 
                         gen_number, dd_desired_allocation (dynamic_data_of (gen_number))));
            current_gc_data_per_heap->gen_data[gen_number].size_after = generation_size (gen_number);
            current_gc_data_per_heap->gen_data[gen_number].free_list_space_after = generation_free_list_space (generation_of (gen_number));
            current_gc_data_per_heap->gen_data[gen_number].free_obj_space_after = generation_free_obj_space (generation_of (gen_number));
        }
    }
    else
#endif //BACKGROUND_GC
    {
        free_list_info (max_generation, "end");
        for (int gen_number = 0; gen_number <= n; gen_number++)
        {
            dynamic_data* dd = dynamic_data_of (gen_number);
            dd_gc_elapsed_time (dd) = end_gc_time - dd_time_clock (dd);
            compute_new_dynamic_data (gen_number);
        }

        if (n != max_generation)
        {
            int gen_num_for_data = ((n < (max_generation - 1)) ? (n + 1) : (max_generation + 1));
            for (int gen_number = (n + 1); gen_number <= gen_num_for_data; gen_number++)
            {
                get_gc_data_per_heap()->gen_data[gen_number].size_after = generation_size (gen_number);
                get_gc_data_per_heap()->gen_data[gen_number].free_list_space_after = generation_free_list_space (generation_of (gen_number));
                get_gc_data_per_heap()->gen_data[gen_number].free_obj_space_after = generation_free_obj_space (generation_of (gen_number));
            }
        }

        get_gc_data_per_heap()->maxgen_size_info.running_free_list_efficiency = (uint32_t)(generation_allocator_efficiency (generation_of (max_generation)) * 100);

        free_list_info (max_generation, "after computing new dynamic data");
        
        if (heap_number == 0)
        {
            dprintf (GTC_LOG, ("GC#%d(gen%d) took %Idms", 
                dd_collection_count (dynamic_data_of (0)), 
                settings.condemned_generation,
                dd_gc_elapsed_time (dynamic_data_of (0))));
        }

        for (int gen_number = 0; gen_number <= (max_generation + 1); gen_number++)
        {
            dprintf (2, ("end of FGC/NGC: gen%d new_alloc: %Id", 
                         gen_number, dd_desired_allocation (dynamic_data_of (gen_number))));
        }
    }

    // 更新收集代+1代的動態數據(dd)
    if (n < max_generation)
    {
        compute_promoted_allocation (1 + n);

        dynamic_data* dd = dynamic_data_of (1 + n);
        size_t new_fragmentation = generation_free_list_space (generation_of (1 + n)) + 
                                   generation_free_obj_space (generation_of (1 + n));

#ifdef BACKGROUND_GC
        if (current_c_gc_state != c_gc_state_planning)
#endif //BACKGROUND_GC
        {
            if (settings.promotion)
            {
                dd_fragmentation (dd) = new_fragmentation;
            }
            else
            {
                //assert (dd_fragmentation (dd) == new_fragmentation);
            }
        }
    }

    // 更新ephemeral_low(gen 1的開始的地址)和ephemeral_high(ephemeral_heap_segment的預留地址)
#ifdef BACKGROUND_GC
    if (!settings.concurrent)
#endif //BACKGROUND_GC
    {
        adjust_ephemeral_limits(!!IsGCThread());
    }

#ifdef BACKGROUND_GC
    assert (ephemeral_low == generation_allocation_start (generation_of ( max_generation -1)));
    assert (ephemeral_high == heap_segment_reserved (ephemeral_heap_segment));
#endif //BACKGROUND_GC

    // 若是fgn_maxgen_percent有設置而且收集的是代1則檢查是否要收集代2, 不然通知full_gc_end_event事件
    if (fgn_maxgen_percent)
    {
        if (settings.condemned_generation == (max_generation - 1))
        {
            check_for_full_gc (max_generation - 1, 0);
        }
        else if (settings.condemned_generation == max_generation)
        {
            if (full_gc_approach_event_set 
#ifdef MULTIPLE_HEAPS
                && (heap_number == 0)
#endif //MULTIPLE_HEAPS
                )
            {
                dprintf (2, ("FGN-GC: setting gen2 end event"));

                full_gc_approach_event.Reset();
#ifdef BACKGROUND_GC
                // By definition WaitForFullGCComplete only succeeds if it's full, *blocking* GC, otherwise need to return N/A
                fgn_last_gc_was_concurrent = settings.concurrent ? TRUE : FALSE;
#endif //BACKGROUND_GC
                full_gc_end_event.Set();
                full_gc_approach_event_set = false;            
            }
        }
    }

    // 從新決定分配量(allocation_quantum)
    // 這裏的 dd_new_allocation 已經從新設置過
    // 分配量 = 離下次啓動gc須要分配的大小 / (2 * 已用的分配上下文數量), 最小1K, 最大8K
    // 若是很快就要從新啓動gc, 或者用的分配上下文較多(浪費較多), 則須要減小分配量
    // 大部分狀況下這裏的分配量都會設置爲默認的8K
#ifdef BACKGROUND_GC
    if (!settings.concurrent)
#endif //BACKGROUND_GC
    {
        //decide on the next allocation quantum
        if (alloc_contexts_used >= 1)
        {
            allocation_quantum = Align (min ((size_t)CLR_SIZE,
                                            (size_t)max (1024, get_new_allocation (0) / (2 * alloc_contexts_used))),
                                            get_alignment_constant(FALSE));
            dprintf (3, ("New allocation quantum: %d(0x%Ix)", allocation_quantum, allocation_quantum));
        }
    }
    // 重設Write Watch,默認會用Write barrier因此這裏不會被調用
#ifdef NO_WRITE_BARRIER
    reset_write_watch(FALSE);
#endif //NO_WRITE_BARRIER

    // 打印出錯信息
    descr_generations (FALSE);
    descr_card_table();

    // 驗證小對象的segment列表(gen0~2的segment),除錯用
    verify_soh_segment_list();

#ifdef BACKGROUND_GC
    add_to_history_per_heap();
    if (heap_number == 0)
    {
        add_to_history();
    }
#endif // BACKGROUND_GC

#ifdef GC_STATS
    if (GCStatistics::Enabled() && heap_number == 0)
        g_GCStatistics.AddGCStats(settings, 
            dd_gc_elapsed_time(dynamic_data_of(settings.condemned_generation)));
#endif // GC_STATS

#ifdef TIME_GC
    fprintf (stdout, "%d,%d,%d,%d,%d,%d\n",
             n, mark_time, plan_time, reloc_time, compact_time, sweep_time);
#endif //TIME_GC

#ifdef BACKGROUND_GC
    assert (settings.concurrent == (uint32_t)(bgc_thread_id.IsCurrentThread()));
#endif //BACKGROUND_GC

    // 檢查heap狀態,除錯用
    // 若是是後臺gc還須要中止運行引擎,驗證完之後再重啓
#if defined(VERIFY_HEAP) || (defined (FEATURE_EVENT_TRACE) && defined(BACKGROUND_GC))
    if (FALSE 
#ifdef VERIFY_HEAP
        // Note that right now g_pConfig->GetHeapVerifyLevel always returns the same
        // value. If we ever allow randomly adjusting this as the process runs,
        // we cannot call it this way as joins need to match - we must have the same
        // value for all heaps like we do with bgc_heap_walk_for_etw_p.
        || (g_pConfig->GetHeapVerifyLevel() & EEConfig::HEAPVERIFY_GC)
#endif
#if defined(FEATURE_EVENT_TRACE) && defined(BACKGROUND_GC)
        || (bgc_heap_walk_for_etw_p && settings.concurrent)
#endif
        )
    {
#ifdef BACKGROUND_GC
        Thread* current_thread = GetThread();
        BOOL cooperative_mode = TRUE;

        if (settings.concurrent)
        {
            cooperative_mode = enable_preemptive (current_thread);

#ifdef MULTIPLE_HEAPS
            bgc_t_join.join(this, gc_join_suspend_ee_verify);
            if (bgc_t_join.joined())
            {
                bgc_threads_sync_event.Reset();

                dprintf(2, ("Joining BGC threads to suspend EE for verify heap"));
                bgc_t_join.restart();
            }
            if (heap_number == 0)
            {
                suspend_EE();
                bgc_threads_sync_event.Set();
            }
            else
            {
                bgc_threads_sync_event.Wait(INFINITE, FALSE);
                dprintf (2, ("bgc_threads_sync_event is signalled"));
            }
#else
            suspend_EE();
#endif //MULTIPLE_HEAPS

            //fix the allocation area so verify_heap can proceed.
            fix_allocation_contexts (FALSE);
        }
#endif //BACKGROUND_GC

#ifdef BACKGROUND_GC
        assert (settings.concurrent == (uint32_t)(bgc_thread_id.IsCurrentThread()));
#ifdef FEATURE_EVENT_TRACE
        if (bgc_heap_walk_for_etw_p && settings.concurrent)
        {
            make_free_lists_for_profiler_for_bgc();
        }
#endif // FEATURE_EVENT_TRACE
#endif //BACKGROUND_GC

#ifdef VERIFY_HEAP
        if (g_pConfig->GetHeapVerifyLevel() & EEConfig::HEAPVERIFY_GC)
            verify_heap (FALSE);
#endif // VERIFY_HEAP

#ifdef BACKGROUND_GC
        if (settings.concurrent)
        {
            repair_allocation_contexts (TRUE);

#ifdef MULTIPLE_HEAPS
            bgc_t_join.join(this, gc_join_restart_ee_verify);
            if (bgc_t_join.joined())
            {
                bgc_threads_sync_event.Reset();

                dprintf(2, ("Joining BGC threads to restart EE after verify heap"));
                bgc_t_join.restart();
            }
            if (heap_number == 0)
            {
                restart_EE();
                bgc_threads_sync_event.Set();
            }
            else
            {
                bgc_threads_sync_event.Wait(INFINITE, FALSE);
                dprintf (2, ("bgc_threads_sync_event is signalled"));
            }
#else
            restart_EE();
#endif //MULTIPLE_HEAPS

            disable_preemptive (current_thread, cooperative_mode);
        }
#endif //BACKGROUND_GC
    }
#endif // defined(VERIFY_HEAP) || (defined(FEATURE_EVENT_TRACE) && defined(BACKGROUND_GC))

    // 若是有多個heap(服務器GC),平均各個heap的閾值(dd_gc_new_allocation, dd_new_allocation, dd_desired_allocation)
    // 其餘服務器GC和工做站GC的共通處理請跳到#else看
#ifdef MULTIPLE_HEAPS
    if (!settings.concurrent)
    {
        gc_t_join.join(this, gc_join_done);
        if (gc_t_join.joined ())
        {
            gc_heap::internal_gc_done = false;

            //equalize the new desired size of the generations
            int limit = settings.condemned_generation;
            if (limit == max_generation)
            {
                limit = max_generation+1;
            }
            for (int gen = 0; gen <= limit; gen++)
            {
                size_t total_desired = 0;

                for (int i = 0; i < gc_heap::n_heaps; i++)
                {
                    gc_heap* hp = gc_heap::g_heaps[i];
                    dynamic_data* dd = hp->dynamic_data_of (gen);
                    size_t temp_total_desired = total_desired + dd_desired_allocation (dd);
                    if (temp_total_desired < total_desired)
                    {
                        // we overflowed.
                        total_desired = (size_t)MAX_PTR;
                        break;
                    }
                    total_desired = temp_total_desired;
                }

                size_t desired_per_heap = Align (total_desired/gc_heap::n_heaps,
                                                    get_alignment_constant ((gen != (max_generation+1))));

                if (gen == 0)
                {
#if 1 //subsumed by the linear allocation model 
                    // to avoid spikes in mem usage due to short terms fluctuations in survivorship,
                    // apply some smoothing.
                    static size_t smoothed_desired_per_heap = 0;
                    size_t smoothing = 3; // exponential smoothing factor
                    if (smoothing  > VolatileLoad(&settings.gc_index))
                        smoothing  = VolatileLoad(&settings.gc_index);
                    smoothed_desired_per_heap = desired_per_heap / smoothing + ((smoothed_desired_per_heap / smoothing) * (smoothing-1));
                    dprintf (1, ("sn = %Id  n = %Id", smoothed_desired_per_heap, desired_per_heap));
                    desired_per_heap = Align(smoothed_desired_per_heap, get_alignment_constant (true));
#endif //0

                    // if desired_per_heap is close to min_gc_size, trim it
                    // down to min_gc_size to stay in the cache
                    gc_heap* hp = gc_heap::g_heaps[0];
                    dynamic_data* dd = hp->dynamic_data_of (gen);
                    size_t min_gc_size = dd_min_gc_size(dd);
                    // if min GC size larger than true on die cache, then don't bother
                    // limiting the desired size
                    if ((min_gc_size <= GCToOSInterface::GetLargestOnDieCacheSize(TRUE) / GCToOSInterface::GetLogicalCpuCount()) &&
                        desired_per_heap <= 2*min_gc_size)
                    {
                        desired_per_heap = min_gc_size;
                    }
#ifdef BIT64
                    desired_per_heap = joined_youngest_desired (desired_per_heap);
                    dprintf (2, ("final gen0 new_alloc: %Id", desired_per_heap));
#endif // BIT64

                    gc_data_global.final_youngest_desired = desired_per_heap;
                }
#if 1 //subsumed by the linear allocation model 
                if (gen == (max_generation + 1))
                {
                    // to avoid spikes in mem usage due to short terms fluctuations in survivorship,
                    // apply some smoothing.
                    static size_t smoothed_desired_per_heap_loh = 0;
                    size_t smoothing = 3; // exponential smoothing factor
                    size_t loh_count = dd_collection_count (dynamic_data_of (max_generation));
                    if (smoothing  > loh_count)
                        smoothing  = loh_count;
                    smoothed_desired_per_heap_loh = desired_per_heap / smoothing + ((smoothed_desired_per_heap_loh / smoothing) * (smoothing-1));
                    dprintf( 2, ("smoothed_desired_per_heap_loh  = %Id  desired_per_heap = %Id", smoothed_desired_per_heap_loh, desired_per_heap));
                    desired_per_heap = Align(smoothed_desired_per_heap_loh, get_alignment_constant (false));
                }
#endif //0
                for (int i = 0; i < gc_heap::n_heaps; i++)
                {
                    gc_heap* hp = gc_heap::g_heaps[i];
                    dynamic_data* dd = hp->dynamic_data_of (gen);
                    dd_desired_allocation (dd) = desired_per_heap;
                    dd_gc_new_allocation (dd) = desired_per_heap;
                    dd_new_allocation (dd) = desired_per_heap;

                    if (gen == 0)
                    {
                        hp->fgn_last_alloc = desired_per_heap;
                    }
                }
            }

#ifdef FEATURE_LOH_COMPACTION
            BOOL all_heaps_compacted_p = TRUE;
#endif //FEATURE_LOH_COMPACTION
            for (int i = 0; i < gc_heap::n_heaps; i++)
            {
                gc_heap* hp = gc_heap::g_heaps[i];
                hp->decommit_ephemeral_segment_pages();
                hp->rearrange_large_heap_segments();
#ifdef FEATURE_LOH_COMPACTION
                all_heaps_compacted_p &= hp->loh_compacted_p;
#endif //FEATURE_LOH_COMPACTION
            }

#ifdef FEATURE_LOH_COMPACTION
            check_loh_compact_mode (all_heaps_compacted_p);
#endif //FEATURE_LOH_COMPACTION

            fire_pevents();

            gc_t_join.restart();
        }
        alloc_context_count = 0;
        heap_select::mark_heap (heap_number);
    }

#else
    // 如下處理服務器GC和工做站共通,你能夠在#else上面找到對應的代碼

    // 設置統計數據(最年輕代的gc閾值)
    gc_data_global.final_youngest_desired = 
        dd_desired_allocation (dynamic_data_of (0));

    // 若是大對象的堆(loh)壓縮模式是僅1次(once)且全部heap的loh都壓縮過則重置loh的壓縮模式
    check_loh_compact_mode (loh_compacted_p);

    // 釋放ephemeral segment中未用到的內存(頁)
    decommit_ephemeral_segment_pages();

    // 觸發etw事件,統計用
    fire_pevents();

    if (!(settings.concurrent))
    {
        // 刪除空的大對象segment
        rearrange_large_heap_segments();
        // 通知運行引擎GC已完成(GcDone, 目前不會作出實質的處理)而且更新一些統計數據
        do_post_gc();
    }

#ifdef BACKGROUND_GC
    recover_bgc_settings();
#endif //BACKGROUND_GC
#endif //MULTIPLE_HEAPS
}

接下來咱們將分別分析GC中的五個階段(mark_phase, plan_phase, relocate_phase, compact_phase, sweep_phase)的內部處理

標記階段(mark_phase)

這個階段的做用是找出收集垃圾的範圍(gc_low ~ gc_high)中有哪些對象是存活的,若是存活則標記(m_pMethTab |= 1),
另外還會根據GC Handle查找有哪些對象是固定的(pinned),若是對象固定則標記(m_uSyncBlockValue |= 0x20000000)。

簡單解釋下GC Handle和Pinned Object,GC Handle用於在託管代碼中調用非託管代碼時能夠決定傳遞的指針的處理,
一個類型是Pinned的GC Handle能夠防止GC在壓縮時移動對象,這樣非託管代碼中保存的指針地址不會失效,詳細能夠看微軟的文檔

在繼續看代碼以前咱們先來了解Card Table的概念:

Card Table

若是你以前已經瞭解過GC,可能知道有的語言實現GC會有一個根對象,從根對象一直掃描下去能夠找到全部存活的對象。

但這樣有一個缺陷,若是對象不少,掃描的時間也會相應的變長,爲了提升效率,CoreCLR使用了分代GC(包括以前的.Net Framework都是分代GC),
分代GC能夠只選擇掃描一部分的對象(年輕的對象更有可能被回收)而不是所有對象,那麼分代GC的掃描是如何實現的?

在CoreCLR中對象之間的引用(例如B是A的成員或者B在數組A中,能夠稱做A引用B)通常包含如下狀況

  • 各個線程棧(stack)和寄存器(register)中的對象引用堆段(heap segment)中的對象
    • CoreCLR有辦法能夠檢測到Managed Thread中在棧和寄存器中的對象
    • 這些對象是根對象(GC Root)的一種
  • GC Handle表中的句柄引用堆段(heap segment)中的對象
    • 這些對象也是根對象的一種
  • 析構隊列中的對象引用堆段(heap segment)中的對象
    • 這些對象也是根對象的一種
  • 同代對象之間的引用
  • 隔代對象之間的引用

請考慮下圖的狀況,咱們此次只想掃描gen 0,棧中的對象A引用了gen 1的對象B,對象B引用了gen 0的對象C,
在掃描的時候由於B不在掃描範圍(gc_low ~ gc_high)中,CoreCLR不會去繼續跟蹤B的引用,
若是這時候gen 0中無其餘對象引用對象C,是否會致使對象C被誤回收?

爲了解決這種狀況致使的問題,CoreCLR使用了Card Table,所謂Card Table就是專門記錄跨代引用的一個數組

當咱們設置B.member = C的時候,JIT會把賦值替換爲JIT_WriteBarrier(&B.member, C)(或同等的其餘函數)
JIT_WriteBarrier函數中會設置*dst = ref,而且若是refephemeral heap segment中(ref多是gen 0或gen 1的對象)時,
設置dst在Card Table中所屬的字節爲0xff,Card Table中一個字節默認涵蓋的範圍在32位下是1024字節,在64位下是2048字節。
須要注意的是這裏的dstB.member的地址而不是B的地址,B.member的地址會是B的地址加必定的偏移值,
B自身的地址不必定會在Card Table中獲得標記,咱們以後能夠根據B.member的地址獲得B的地址(能夠看find_first_object函數)。

有了Card Table之後,只回收年輕代(非Full GC)時除了掃描根對象之外咱們還須要掃描Card Table中標記的範圍來防止誤回收對象。

JIT_WriteBarrier函數的代碼以下

// This function is a JIT helper, but it must NOT use HCIMPL2 because it
// modifies Thread state that will not be restored if an exception occurs
// inside of memset.  A normal EH unwind will not occur.
extern "C" HCIMPL2_RAW(VOID, JIT_WriteBarrier, Object **dst, Object *ref)
{
    // Must use static contract here, because if an AV occurs, a normal EH
    // unwind will not occur, and destructors will not run.
    STATIC_CONTRACT_MODE_COOPERATIVE;
    STATIC_CONTRACT_THROWS;
    STATIC_CONTRACT_GC_NOTRIGGER;
    
#ifdef FEATURE_COUNT_GC_WRITE_BARRIERS
    IncUncheckedBarrierCount();
#endif
    // no HELPER_METHOD_FRAME because we are MODE_COOPERATIVE, GC_NOTRIGGER
    
    *dst = ref;

    // If the store above succeeded, "dst" should be in the heap.
   assert(GCHeap::GetGCHeap()->IsHeapPointer((void*)dst));

#ifdef WRITE_BARRIER_CHECK
    updateGCShadow(dst, ref);     // support debugging write barrier
#endif
    
#ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
    if (SoftwareWriteWatch::IsEnabledForGCHeap())
    {
        SoftwareWriteWatch::SetDirty(dst, sizeof(*dst));
    }
#endif // FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP

#ifdef FEATURE_COUNT_GC_WRITE_BARRIERS
    if((BYTE*) dst >= g_ephemeral_low && (BYTE*) dst < g_ephemeral_high)
    {
        UncheckedDestInEphem++;
    }
#endif
    if((BYTE*) ref >= g_ephemeral_low && (BYTE*) ref < g_ephemeral_high)
    {
#ifdef FEATURE_COUNT_GC_WRITE_BARRIERS
        UncheckedAfterRefInEphemFilter++;
#endif
        BYTE* pCardByte = (BYTE *)VolatileLoadWithoutBarrier(&g_card_table) + card_byte((BYTE *)dst);
        if(*pCardByte != 0xFF)
        {
#ifdef FEATURE_COUNT_GC_WRITE_BARRIERS
            UncheckedAfterAlreadyDirtyFilter++;
#endif
            *pCardByte = 0xFF;
        }
    }
}
HCIMPLEND_RAW

card_byte macro的代碼以下

#if defined(_WIN64)
// Card byte shift is different on 64bit.
#define card_byte_shift     11
#else
#define card_byte_shift     10
#endif

#define card_byte(addr) (((size_t)(addr)) >> card_byte_shift)
#define card_bit(addr)  (1 << ((((size_t)(addr)) >> (card_byte_shift - 3)) & 7))

標記階段(mark_phase)的代碼

gc_heap::mark_phase函數的代碼以下:

void gc_heap::mark_phase (int condemned_gen_number, BOOL mark_only_p)
{
    assert (settings.concurrent == FALSE);

    // 掃描上下文
    ScanContext sc;
    sc.thread_number = heap_number;
    sc.promotion = TRUE;
    sc.concurrent = FALSE;

    dprintf(2,("---- Mark Phase condemning %d ----", condemned_gen_number));
    // 是否Full GC
    BOOL  full_p = (condemned_gen_number == max_generation);

    // 統計標記階段的開始時間
#ifdef TIME_GC
    unsigned start;
    unsigned finish;
    start = GetCycleCount32();
#endif //TIME_GC

    // 重置動態數據(dd)
    int gen_to_init = condemned_gen_number;
    if (condemned_gen_number == max_generation)
    {
        gen_to_init = max_generation + 1;
    }
    for (int gen_idx = 0; gen_idx <= gen_to_init; gen_idx++)
    {
        dynamic_data* dd = dynamic_data_of (gen_idx);
        dd_begin_data_size (dd) = generation_size (gen_idx) - 
                                   dd_fragmentation (dd) -
                                   Align (size (generation_allocation_start (generation_of (gen_idx))));
        dprintf (2, ("begin data size for gen%d is %Id", gen_idx, dd_begin_data_size (dd)));
        dd_survived_size (dd) = 0;
        dd_pinned_survived_size (dd) = 0;
        dd_artificial_pinned_survived_size (dd) = 0;
        dd_added_pinned_size (dd) = 0;
#ifdef SHORT_PLUGS
        dd_padding_size (dd) = 0;
#endif //SHORT_PLUGS
#if defined (RESPECT_LARGE_ALIGNMENT) || defined (FEATURE_STRUCTALIGN)
        dd_num_npinned_plugs (dd) = 0;
#endif //RESPECT_LARGE_ALIGNMENT || FEATURE_STRUCTALIGN
    }

#ifdef FFIND_OBJECT
    if (gen0_must_clear_bricks > 0)
        gen0_must_clear_bricks--;
#endif //FFIND_OBJECT

    size_t last_promoted_bytes = 0;

    // 重設mark stack
    // mark_stack_array在GC各個階段有不一樣的用途,在mark phase中的用途是用來標記對象時代替遞歸防止爆棧
    promoted_bytes (heap_number) = 0;
    reset_mark_stack();

#ifdef SNOOP_STATS
    memset (&snoop_stat, 0, sizeof(snoop_stat));
    snoop_stat.heap_index = heap_number;
#endif //SNOOP_STATS

    // 啓用scable marking時
    // 服務器GC上會啓用,工做站GC上不會啓用
    // scable marking這篇中不會分析
#ifdef MH_SC_MARK
    if (full_p)
    {
        //initialize the mark stack
        for (int i = 0; i < max_snoop_level; i++)
        {
            ((uint8_t**)(mark_stack_array))[i] = 0;
        }

        mark_stack_busy() = 1;
    }
#endif //MH_SC_MARK

    static uint32_t num_sizedrefs = 0;

    // scable marking的處理
#ifdef MH_SC_MARK
    static BOOL do_mark_steal_p = FALSE;
#endif //MH_SC_MARK

#ifdef MULTIPLE_HEAPS
    gc_t_join.join(this, gc_join_begin_mark_phase);
    if (gc_t_join.joined())
    {
#endif //MULTIPLE_HEAPS

        num_sizedrefs = SystemDomain::System()->GetTotalNumSizedRefHandles();

#ifdef MULTIPLE_HEAPS

    // scable marking的處理
#ifdef MH_SC_MARK
        if (full_p)
        {
            size_t total_heap_size = get_total_heap_size();

            if (total_heap_size > (100 * 1024 * 1024))
            {
                do_mark_steal_p = TRUE;
            }
            else
            {
                do_mark_steal_p = FALSE;
            }
        }
        else
        {
            do_mark_steal_p = FALSE;
        }
#endif //MH_SC_MARK

        gc_t_join.restart();
    }
#endif //MULTIPLE_HEAPS

    {

    // 初始化mark list, full gc時不會使用
#ifdef MARK_LIST
        //set up the mark lists from g_mark_list
        assert (g_mark_list);
#ifdef MULTIPLE_HEAPS
        mark_list = &g_mark_list [heap_number*mark_list_size];
#else
        mark_list = g_mark_list;
#endif //MULTIPLE_HEAPS
        //dont use the mark list for full gc
        //because multiple segments are more complex to handle and the list
        //is likely to overflow
        if (condemned_gen_number != max_generation)
            mark_list_end = &mark_list [mark_list_size-1];
        else
            mark_list_end = &mark_list [0];
        mark_list_index = &mark_list [0];
#endif //MARK_LIST

        shigh = (uint8_t*) 0;
        slow  = MAX_PTR;

        //%type%  category = quote (mark);
        // 若是當前是Full GC而且有類型是SizedRef的GC Handle時把它們做爲根對象掃描
        // 參考https://github.com/dotnet/coreclr/blob/release/1.1.0/src/gc/objecthandle.h#L177
        // SizedRef是一個非公開類型的GC Handle(其餘還有RefCounted),目前還看不到有代碼使用
        if ((condemned_gen_number == max_generation) && (num_sizedrefs > 0))
        {
            GCScan::GcScanSizedRefs(GCHeap::Promote, condemned_gen_number, max_generation, &sc);
            fire_mark_event (heap_number, ETW::GC_ROOT_SIZEDREF, (promoted_bytes (heap_number) - last_promoted_bytes));
            last_promoted_bytes = promoted_bytes (heap_number);

#ifdef MULTIPLE_HEAPS
            gc_t_join.join(this, gc_join_scan_sizedref_done);
            if (gc_t_join.joined())
            {
                dprintf(3, ("Done with marking all sized refs. Starting all gc thread for marking other strong roots"));
                gc_t_join.restart();
            }
#endif //MULTIPLE_HEAPS
        }
    
        dprintf(3,("Marking Roots"));

        // 掃描根對象(各個線程中棧和寄存器中的對象)
        // 這裏的GcScanRoots是一個高階函數,會掃描根對象和根對象引用的對象,並對它們調用傳入的`GCHeap::Promote`函數
        // 在下面的relocate phase還會傳入`GCHeap::Relocate`給`GcScanRoots`
        // BOTR中有一份專門的文檔介紹瞭如何實現棧掃描,地址是
        // https://github.com/dotnet/coreclr/blob/master/Documentation/botr/stackwalking.md
        // 這個函數的內部處理要貼代碼的話會很是的長,這裏我只貼調用流程
        // GcScanRoots的處理
        // 枚舉線程
        // 調用 ScanStackRoots(pThread, fn, sc);
        //    調用 pThread->StackWalkFrames
        //        調用 StackWalkFramesEx
        //            使用 StackFrameIterator 枚舉棧中的全部幀
        //                調用 StackFrameIterator::Next
        //                    調用 StackFrameIterator::Filter
        //                調用 MakeStackwalkerCallback 處理單幀
        //                    調用 GcStackCrawlCallBack
        //                        若是 IsFrameless 則調用 EECodeManager::EnumGcRefs
        //                            調用 GcInfoDecoder::EnumerateLiveSlots
        //                                調用 GcInfoDecoder::ReportSlotToGC
        //                                    若是是寄存器中的對象則調用 GcInfoDecoder::ReportRegisterToGC
        //                                    若是是棧上的對象則調用 GcInfoDecoder::ReportStackSlotToGC
        //                                        調用 GcEnumObject
        //                                            調用 GCHeap::Promote, 接下來和下面的同樣
        //                        若是 !IsFrameless 則調用 FrameBase::GcScanRoots
        //                        繼承函數的處理 GCFrame::GcScanRoots
        //                            調用 GCHeap::Promote
        //                                調用 gc_heap::mark_object_simple
        //                                    調用 gc_mark1, 第一次標記時會返回true
        //                                        調用 CObjectHeader::IsMarked !!(((size_t)RawGetMethodTable()) & GC_MARKED)
        //                                        調用 CObjectHeader::SetMarked RawSetMethodTable((MethodTable *) (((size_t) RawGetMethodTable()) | GC_MARKED));
        //                                    若是對象未被標記過,調用 go_through_object_cl (macro) 枚舉對象的全部成員
        //                                        對成員對象調用mark_object_simple1,和mark_object_simple的區別是,mark_object_simple1使用mark_stack_array來循環標記對象
        //                                        使用mark_stack_array代替遞歸能夠防止爆棧
        //                                        注意mark_stack_array也有大小限制,若是超過了(overflow)不會擴展(grow),而是記錄並交給下面的GcDhInitialScan處理
        GCScan::GcScanRoots(GCHeap::Promote,
                                condemned_gen_number, max_generation,
                                &sc);

        // 調用通知事件通知有多少字節在這一次被標記
        fire_mark_event (heap_number, ETW::GC_ROOT_STACK, (promoted_bytes (heap_number) - last_promoted_bytes));
        last_promoted_bytes = promoted_bytes (heap_number);

#ifdef BACKGROUND_GC
        if (recursive_gc_sync::background_running_p())
        {
            scan_background_roots (GCHeap::Promote, heap_number, &sc);
        }
#endif //BACKGROUND_GC

        // 掃描當前關鍵析構(Critical Finalizer)隊列中對象的引用
        // 非關鍵析構隊列中的對象會在下面的ScanForFinalization中掃描
        // 關於析構隊列能夠參考這些URL
        // https://github.com/dotnet/coreclr/blob/master/Documentation/botr/threading.md
        // http://stackoverflow.com/questions/1268525/what-are-the-finalizer-queue-and-controlthreadmethodentry
        // http://stackoverflow.com/questions/9030126/why-classes-with-finalizers-need-more-than-one-garbage-collection-cycle
        // https://msdn.microsoft.com/en-us/library/system.runtime.constrainedexecution.criticalfinalizerobject(v=vs.110).aspx
        // https://msdn.microsoft.com/en-us/library/system.runtime.constrainedexecution(v=vs.110).aspx
#ifdef FEATURE_PREMORTEM_FINALIZATION
        dprintf(3, ("Marking finalization data"));
        finalize_queue->GcScanRoots(GCHeap::Promote, heap_number, 0);
#endif // FEATURE_PREMORTEM_FINALIZATION

        // 調用通知事件通知有多少字節在這一次被標記
        fire_mark_event (heap_number, ETW::GC_ROOT_FQ, (promoted_bytes (heap_number) - last_promoted_bytes));
        last_promoted_bytes = promoted_bytes (heap_number);

// MTHTS
        {
            // 掃描GC Handle引用的對象
            // 若是GC Handle的類型是Pinned同時會設置對象爲pinned
            // 設置對象爲pinned的流程以下
            // GCScan::GcScanHandles
            //    Ref_TracePinningRoots
            //        HndScanHandlersForGC
            //            TableScanHandles
            //                SegmentScanByTypeMap
            //                    BlockScanBlocksEphemeral
            //                        BlockScanBlocksEphemeralWorker
            //                            ScanConsecutiveHandlesWithoutUserData
            //                                PinObject
            //                                    GCHeap::Promote(pRef, (ScanContext *)lpl, GC_CALL_PINNED)
            //                                        判斷flags包含GC_CALL_PINNED時調用 gc_heap::pin_object
            //                                            若是對象在掃描範圍(gc_low ~ gc_high)時調用set_pinned(o)
            //                                                GetHeader()->SetGCBit()
            //                                                    m_uSyncBlockValue |= BIT_SBLK_GC_RESERVE
            // 這裏會標記包括來源於靜態字段的引用
            dprintf(3,("Marking handle table"));
            GCScan::GcScanHandles(GCHeap::Promote,
                                      condemned_gen_number, max_generation,
                                      &sc);
            // 調用通知事件通知有多少字節在這一次被標記
            fire_mark_event (heap_number, ETW::GC_ROOT_HANDLES, (promoted_bytes (heap_number) - last_promoted_bytes));
            last_promoted_bytes = promoted_bytes (heap_number);
        }

        // 掃描根對象完成了,若是不是Full GC接下來還須要掃描Card Table
        // 記錄掃描Card Table以前標記的字節數量(存活的字節數量)
#ifdef TRACE_GC
        size_t promoted_before_cards = promoted_bytes (heap_number);
#endif //TRACE_GC

        // Full GC不須要掃Card Table
        dprintf (3, ("before cards: %Id", promoted_before_cards));
        if (!full_p)
        {
#ifdef CARD_BUNDLE
#ifdef MULTIPLE_HEAPS
            if (gc_t_join.r_join(this, gc_r_join_update_card_bundle))
            {
#endif //MULTIPLE_HEAPS

                // 從Write Watch更新Card Table的索引(Card Bundles)
                // 當內存空間過大時,掃描Card Table的效率會變低,使用Card Bundle能夠標記Card Table中的哪些區域須要掃描
                // 在做者環境的下Card Bundle不啓用
                update_card_table_bundle ();

#ifdef MULTIPLE_HEAPS
                gc_t_join.r_restart();
            }
#endif //MULTIPLE_HEAPS
#endif //CARD_BUNDLE

            // 標記對象的函數,須要分析時使用特殊的函數
            card_fn mark_object_fn = &gc_heap::mark_object_simple;
#ifdef HEAP_ANALYZE
            heap_analyze_success = TRUE;
            if (heap_analyze_enabled)
            {
                internal_root_array_index = 0;
                current_obj = 0;
                current_obj_size = 0;
                mark_object_fn = &gc_heap::ha_mark_object_simple;
            }
#endif //HEAP_ANALYZE

            // 遍歷Card Table標記小對象
            // 像以前所說的Card Table中對應的區域包含的是成員的地址,不必定包含來源對象的開始地址,find_first_object函數能夠支持找到來源對象的開始地址
            // 這個函數除了調用mark_object_simple標記找到的對象之外,還會更新`generation_skip_ratio`這個成員,算法以下
            //  n_gen 經過卡片標記的對象數量, gc_low ~ gc_high
            //  n_eph 經過卡片掃描的對象數量, 上一代的開始地址 ~ gc_high (cg_pointers_found的累加)
            //    表示掃描的對象中有多少%的對象被標記了
            //    generation_skip_ratio = (n_eph > 400) ? (n_gen * 1.0 / n_eph * 100) : 100
            // `generation_skip_ratio`會影響到對象是否升代,請搜索上面關於`generation_skip_ratio`的註釋
            dprintf(3,("Marking cross generation pointers"));
            mark_through_cards_for_segments (mark_object_fn, FALSE);

            // 遍歷Card Table標記大對象
            // 處理和前面同樣,只是掃描的範圍是大對象的segment
            // 這裏也會算出generation_skip_ratio,若是算出的generation_skip_ratio比原來的generation_skip_ratio要小則使用算出的值
            dprintf(3,("Marking cross generation pointers for large objects"));
            mark_through_cards_for_large_objects (mark_object_fn, FALSE);

            // 調用通知事件通知有多少字節在這一次被標記
            dprintf (3, ("marked by cards: %Id", 
                (promoted_bytes (heap_number) - promoted_before_cards)));
            fire_mark_event (heap_number, ETW::GC_ROOT_OLDER, (promoted_bytes (heap_number) - last_promoted_bytes));
            last_promoted_bytes = promoted_bytes (heap_number);
        }
    }

    // scable marking的處理
#ifdef MH_SC_MARK
    if (do_mark_steal_p)
    {
        mark_steal();
    }
#endif //MH_SC_MARK

    // 處理HNDTYPE_DEPENDENT類型的GC Handle
    // 這個GC Handle的意義是保存兩個對象primary和secondary,告訴primary引用了secondary
    // 若是primary已標記則secondary也會被標記
    // 這裏還會處理以前發生的mark_stack_array溢出(循環標記對象時子對象過多致使mark_stack_array容不下)
    // 此次不必定會完成,下面還會等待線程同步後(服務器GC下)再掃一遍
    // Dependent handles need to be scanned with a special algorithm (see the header comment on
    // scan_dependent_handles for more detail). We perform an initial scan without synchronizing with other
    // worker threads or processing any mark stack overflow. This is not guaranteed to complete the operation
    // but in a common case (where there are no dependent handles that are due to be collected) it allows us
    // to optimize away further scans. The call to scan_dependent_handles is what will cycle through more
    // iterations if required and will also perform processing of any mark stack overflow once the dependent
    // handle table has been fully promoted.
    GCScan::GcDhInitialScan(GCHeap::Promote, condemned_gen_number, max_generation, &sc);
    scan_dependent_handles(condemned_gen_number, &sc, true);

    // 通知標記階段完成掃描根對象(和Card Table)
#ifdef MULTIPLE_HEAPS
    dprintf(3, ("Joining for short weak handle scan"));
    gc_t_join.join(this, gc_join_null_dead_short_weak);
    if (gc_t_join.joined())
#endif //MULTIPLE_HEAPS
    {
#ifdef HEAP_ANALYZE
        heap_analyze_enabled = FALSE;
        DACNotifyGcMarkEnd(condemned_gen_number);
#endif // HEAP_ANALYZE
        GCToEEInterface::AfterGcScanRoots (condemned_gen_number, max_generation, &sc);

#ifdef MULTIPLE_HEAPS
        if (!full_p)
        {
            // we used r_join and need to reinitialize states for it here.
            gc_t_join.r_init();
        }

        //start all threads on the roots.
        dprintf(3, ("Starting all gc thread for short weak handle scan"));
        gc_t_join.restart();
#endif //MULTIPLE_HEAPS

    }

    // 處理HNDTYPE_WEAK_SHORT類型的GC Handle
    // 設置未被標記的對象的弱引用(Weak Reference)爲null
    // 這裏傳的GCHeap::Promote參數不會被用到
    // 下面掃描完非關鍵析構隊列還會掃描HNDTYPE_WEAK_LONG類型的GC Handle,請看下面的註釋
    // null out the target of short weakref that were not promoted.
    GCScan::GcShortWeakPtrScan(GCHeap::Promote, condemned_gen_number, max_generation,&sc);

// MTHTS: keep by single thread
#ifdef MULTIPLE_HEAPS
    dprintf(3, ("Joining for finalization"));
    gc_t_join.join(this, gc_join_scan_finalization);
    if (gc_t_join.joined())
#endif //MULTIPLE_HEAPS

    {
#ifdef MULTIPLE_HEAPS
        //start all threads on the roots.
        dprintf(3, ("Starting all gc thread for Finalization"));
        gc_t_join.restart();
#endif //MULTIPLE_HEAPS
    }

    //Handle finalization.
    size_t promoted_bytes_live = promoted_bytes (heap_number);

    // 掃描當前非關鍵析構隊列中對象的引用
#ifdef FEATURE_PREMORTEM_FINALIZATION
    dprintf (3, ("Finalize marking"));
    finalize_queue->ScanForFinalization (GCHeap::Promote, condemned_gen_number, mark_only_p, __this);

#ifdef GC_PROFILING
    if (CORProfilerTrackGC())
    {
        finalize_queue->WalkFReachableObjects (__this);
    }
#endif //GC_PROFILING
#endif // FEATURE_PREMORTEM_FINALIZATION

    // 再掃一遍HNDTYPE_DEPENDENT類型的GC Handle
    // Scan dependent handles again to promote any secondaries associated with primaries that were promoted
    // for finalization. As before scan_dependent_handles will also process any mark stack overflow.
    scan_dependent_handles(condemned_gen_number, &sc, false);

#ifdef MULTIPLE_HEAPS
    dprintf(3, ("Joining for weak pointer deletion"));
    gc_t_join.join(this, gc_join_null_dead_long_weak);
    if (gc_t_join.joined())
    {
        //start all threads on the roots.
        dprintf(3, ("Starting all gc thread for weak pointer deletion"));
        gc_t_join.restart();
    }
#endif //MULTIPLE_HEAPS
    
    // 處理HNDTYPE_WEAK_LONG或HNDTYPE_REFCOUNTED類型的GC Handle
    // 設置未被標記的對象的弱引用(Weak Reference)爲null
    // 這裏傳的GCHeap::Promote參數不會被用到
    // HNDTYPE_WEAK_LONG和HNDTYPE_WEAK_SHORT的區別是,HNDTYPE_WEAK_SHORT會忽略從非關鍵析構隊列的引用而HNDTYPE_WEAK_LONG不會
    // null out the target of long weakref that were not promoted.
    GCScan::GcWeakPtrScan (GCHeap::Promote, condemned_gen_number, max_generation, &sc);

    // 若是使用了mark list而且並行化(服務器GC下)這裏會進行排序(若是定義了PARALLEL_MARK_LIST_SORT)
// MTHTS: keep by single thread
#ifdef MULTIPLE_HEAPS
#ifdef MARK_LIST
#ifdef PARALLEL_MARK_LIST_SORT
//    unsigned long start = GetCycleCount32();
    sort_mark_list();
//    printf("sort_mark_list took %u cycles\n", GetCycleCount32() - start);
#endif //PARALLEL_MARK_LIST_SORT
#endif //MARK_LIST

    dprintf (3, ("Joining for sync block cache entry scanning"));
    gc_t_join.join(this, gc_join_null_dead_syncblk);
    if (gc_t_join.joined())
#endif //MULTIPLE_HEAPS
    {
        // 刪除再也不使用的同步索引塊,而且設置對應對象的索引值爲0
        // scan for deleted entries in the syncblk cache
        GCScan::GcWeakPtrScanBySingleThread (condemned_gen_number, max_generation, &sc);

#ifdef FEATURE_APPDOMAIN_RESOURCE_MONITORING
        if (g_fEnableARM)
        {
            size_t promoted_all_heaps = 0;
#ifdef MULTIPLE_HEAPS
            for (int i = 0; i < n_heaps; i++)
            {
                promoted_all_heaps += promoted_bytes (i);
            }
#else
            promoted_all_heaps = promoted_bytes (heap_number);
#endif //MULTIPLE_HEAPS
            // 記錄此次標記(存活)的字節數
            SystemDomain::RecordTotalSurvivedBytes (promoted_all_heaps);
        }
#endif //FEATURE_APPDOMAIN_RESOURCE_MONITORING

#ifdef MULTIPLE_HEAPS
        // 如下是服務器GC下的處理
        // 若是使用了mark list而且並行化(服務器GC下)這裏會進行壓縮並排序(若是不定義PARALLEL_MARK_LIST_SORT)
#ifdef MARK_LIST
#ifndef PARALLEL_MARK_LIST_SORT
        //compact g_mark_list and sort it.
        combine_mark_lists();
#endif //PARALLEL_MARK_LIST_SORT
#endif //MARK_LIST

        // 若是以前未決定要升代,這裏再給一次機會判斷是否要升代
        // 算法分析
        //     dd_min_gc_size是每分配多少byte的對象就觸發gc的閾值
        //     第0代1倍, 第1代2倍, 再乘以0.1合計
        //     dd = 上一代的動態數據
        //     older_gen_size = 上次gc後的對象大小合計 + 從上次gc以來一共新分配了多少byte
        //     若是m > 上一代的大小, 或者本次標記的對象大小 > m則啓用升代
        //     意義是若是上一代太小,或者此次標記(存活)的對象過多則須要升代
        //decide on promotion
        if (!settings.promotion)
        {
            size_t m = 0;
            for (int n = 0; n <= condemned_gen_number;n++)
            {
                m +=  (size_t)(dd_min_gc_size (dynamic_data_of (n))*(n+1)*0.1);
            }

            for (int i = 0; i < n_heaps; i++)
            {
                dynamic_data* dd = g_heaps[i]->dynamic_data_of (min (condemned_gen_number +1,
                                                                     max_generation));
                size_t older_gen_size = (dd_current_size (dd) +
                                         (dd_desired_allocation (dd) -
                                         dd_new_allocation (dd)));

                if ((m > (older_gen_size)) ||
                    (promoted_bytes (i) > m))
                {
                    settings.promotion = TRUE;
                }
            }
        }

        // scable marking的處理
#ifdef SNOOP_STATS
        if (do_mark_steal_p)
        {
            size_t objects_checked_count = 0;
            size_t zero_ref_count = 0;
            size_t objects_marked_count = 0;
            size_t check_level_count = 0;
            size_t busy_count = 0;
            size_t interlocked_count = 0;
            size_t partial_mark_parent_count = 0;
            size_t stolen_or_pm_count = 0; 
            size_t stolen_entry_count = 0; 
            size_t pm_not_ready_count = 0; 
            size_t normal_count = 0;
            size_t stack_bottom_clear_count = 0;

            for (int i = 0; i < n_heaps; i++)
            {
                gc_heap* hp = g_heaps[i];
                hp->print_snoop_stat();
                objects_checked_count += hp->snoop_stat.objects_checked_count;
                zero_ref_count += hp->snoop_stat.zero_ref_count;
                objects_marked_count += hp->snoop_stat.objects_marked_count;
                check_level_count += hp->snoop_stat.check_level_count;
                busy_count += hp->snoop_stat.busy_count;
                interlocked_count += hp->snoop_stat.interlocked_count;
                partial_mark_parent_count += hp->snoop_stat.partial_mark_parent_count;
                stolen_or_pm_count += hp->snoop_stat.stolen_or_pm_count;
                stolen_entry_count += hp->snoop_stat.stolen_entry_count;
                pm_not_ready_count += hp->snoop_stat.pm_not_ready_count;
                normal_count += hp->snoop_stat.normal_count;
                stack_bottom_clear_count += hp->snoop_stat.stack_bottom_clear_count;
            }

            fflush (stdout);

            printf ("-------total stats-------\n");
            printf ("%8s | %8s | %8s | %8s | %8s | %8s | %8s | %8s | %8s | %8s | %8s | %8s\n", 
                "checked", "zero", "marked", "level", "busy", "xchg", "pmparent", "s_pm", "stolen", "nready", "normal", "clear");
            printf ("%8d | %8d | %8d | %8d | %8d | %8d | %8d | %8d | %8d | %8d | %8d | %8d\n",
                objects_checked_count,
                zero_ref_count,
                objects_marked_count,
                check_level_count,
                busy_count,
                interlocked_count,
                partial_mark_parent_count,
                stolen_or_pm_count,
                stolen_entry_count,
                pm_not_ready_count,
                normal_count,
                stack_bottom_clear_count);
        }
#endif //SNOOP_STATS

        //start all threads.
        dprintf(3, ("Starting all threads for end of mark phase"));
        gc_t_join.restart();
#else //MULTIPLE_HEAPS
        // 如下是工做站GC下的處理

        // 若是以前未決定要升代,這裏再給一次機會判斷是否要升代
        // 算法和前面同樣,可是不是乘以0.1而是乘以0.06
        //decide on promotion
        if (!settings.promotion)
        {
            size_t m = 0;
            for (int n = 0; n <= condemned_gen_number;n++)
            {
                m +=  (size_t)(dd_min_gc_size (dynamic_data_of (n))*(n+1)*0.06);
            }
            dynamic_data* dd = dynamic_data_of (min (condemned_gen_number +1,
                                                     max_generation));
            size_t older_gen_size = (dd_current_size (dd) +
                                     (dd_desired_allocation (dd) -
                                     dd_new_allocation (dd)));

            dprintf (2, ("promotion threshold: %Id, promoted bytes: %Id size n+1: %Id",
                         m, promoted_bytes (heap_number), older_gen_size));

            if ((m > older_gen_size) ||
                    (promoted_bytes (heap_number) > m))
            {
                settings.promotion = TRUE;
            }
        }

#endif //MULTIPLE_HEAPS
    }

    // 若是使用了mark list而且並行化(服務器GC下)這裏會進行歸併(若是定義了PARALLEL_MARK_LIST_SORT)
#ifdef MULTIPLE_HEAPS
#ifdef MARK_LIST
#ifdef PARALLEL_MARK_LIST_SORT
//    start = GetCycleCount32();
    merge_mark_lists();
//    printf("merge_mark_lists took %u cycles\n", GetCycleCount32() - start);
#endif //PARALLEL_MARK_LIST_SORT
#endif //MARK_LIST
#endif //MULTIPLE_HEAPS

    // 統計標記的對象大小
#ifdef BACKGROUND_GC
    total_promoted_bytes = promoted_bytes (heap_number);
#endif //BACKGROUND_GC

    promoted_bytes (heap_number) -= promoted_bytes_live;

    // 統計標記階段的結束時間
#ifdef TIME_GC
        finish = GetCycleCount32();
        mark_time = finish - start;
#endif //TIME_GC

    dprintf(2,("---- End of mark phase ----"));
}

接下來咱們看下GCHeap::Promote函數,在plan_phase中掃描到的對象都會調用這個函數進行標記,
這個函數名稱雖然叫Promote可是裏面只負責對對象進行標記,被標記的對象不必定會升代

void GCHeap::Promote(Object** ppObject, ScanContext* sc, uint32_t flags)
{
    THREAD_NUMBER_FROM_CONTEXT;
#ifndef MULTIPLE_HEAPS
    const int thread = 0;
#endif //!MULTIPLE_HEAPS

    uint8_t* o = (uint8_t*)*ppObject;

    if (o == 0)
        return;

#ifdef DEBUG_DestroyedHandleValue
    // we can race with destroy handle during concurrent scan
    if (o == (uint8_t*)DEBUG_DestroyedHandleValue)
        return;
#endif //DEBUG_DestroyedHandleValue

    HEAP_FROM_THREAD;

    gc_heap* hp = gc_heap::heap_of (o);

    dprintf (3, ("Promote %Ix", (size_t)o));

    // 若是傳入的o不必定是對象的開始地址,則須要從新找到o屬於的對象
#ifdef INTERIOR_POINTERS
    if (flags & GC_CALL_INTERIOR)
    {
        if ((o < hp->gc_low) || (o >= hp->gc_high))
        {
            return;
        }
        if ( (o = hp->find_object (o, hp->gc_low)) == 0)
        {
            return;
        }

    }
#endif //INTERIOR_POINTERS

    // 啓用conservative GC時有可能會對自由對象調用這個函數,這裏須要額外判斷
#ifdef FEATURE_CONSERVATIVE_GC
    // For conservative GC, a value on stack may point to middle of a free object.
    // In this case, we don't need to promote the pointer.
    if (g_pConfig->GetGCConservative()
        && ((CObjectHeader*)o)->IsFree())
    {
        return;
    }
#endif

    // 驗證對象是否能夠標記,除錯用
#ifdef _DEBUG
    ((CObjectHeader*)o)->ValidatePromote(sc, flags);
#else 
    UNREFERENCED_PARAMETER(sc);
#endif //_DEBUG

    // 若是須要標記對象固定(pinned)則調用`pin_object`
    // 請看上面對`PinObject`函數的描述
    // `pin_object`函數會設置對象的同步索引塊 |= 0x20000000
    if (flags & GC_CALL_PINNED)
        hp->pin_object (o, (uint8_t**) ppObject, hp->gc_low, hp->gc_high);

    // 若是有特殊的設置則20次固定一次對象
#ifdef STRESS_PINNING
    if ((++n_promote % 20) == 1)
            hp->pin_object (o, (uint8_t**) ppObject, hp->gc_low, hp->gc_high);
#endif //STRESS_PINNING

#ifdef FEATURE_APPDOMAIN_RESOURCE_MONITORING
    size_t promoted_size_begin = hp->promoted_bytes (thread);
#endif //FEATURE_APPDOMAIN_RESOURCE_MONITORING

    // 若是對象在gc範圍中則調用`mark_object_simple`
    // 若是對象不在gc範圍則會跳過,這也是前面提到的須要Card Table的緣由
    if ((o >= hp->gc_low) && (o < hp->gc_high))
    {
        hpt->mark_object_simple (&o THREAD_NUMBER_ARG);
    }

    // 記錄標記的大小
#ifdef FEATURE_APPDOMAIN_RESOURCE_MONITORING
    size_t promoted_size_end = hp->promoted_bytes (thread);
    if (g_fEnableARM)
    {
        if (sc->pCurrentDomain)
        {
            sc->pCurrentDomain->RecordSurvivedBytes ((promoted_size_end - promoted_size_begin), thread);
        }
    }
#endif //FEATURE_APPDOMAIN_RESOURCE_MONITORING

    STRESS_LOG_ROOT_PROMOTE(ppObject, o, o ? header(o)->GetMethodTable() : NULL);
}

再看下mark_object_simple函數

//this method assumes that *po is in the [low. high[ range
void
gc_heap::mark_object_simple (uint8_t** po THREAD_NUMBER_DCL)
{
    uint8_t* o = *po;
#ifdef MULTIPLE_HEAPS
#else  //MULTIPLE_HEAPS
    const int thread = 0;
#endif //MULTIPLE_HEAPS
    {
#ifdef SNOOP_STATS
        snoop_stat.objects_checked_count++;
#endif //SNOOP_STATS

        // gc_mark1會設置對象中指向Method Table的指針 |= 1
        // 若是對象是第一次標記會返回true
        if (gc_mark1 (o))
        {
            // 更新gc_heap的成員slow和shigh(已標記對象的最小和最大地址)
            // 若是使用了mark list則把對象加到mark list中
            m_boundary (o);
            // 記錄已標記的對象大小
            size_t s = size (o);
            promoted_bytes (thread) += s;
            {
                // 枚舉對象o的全部成員,包括o本身
                go_through_object_cl (method_table(o), o, s, poo,
                                        {
                                            uint8_t* oo = *poo;
                                            // 若是成員在gc掃描範圍中則標記該成員
                                            if (gc_mark (oo, gc_low, gc_high))
                                            {
                                                // 若是使用了mark list則把對象加到mark list中
                                                m_boundary (oo);
                                                // 記錄已標記的對象大小
                                                size_t obj_size = size (oo);
                                                promoted_bytes (thread) += obj_size;
                                                // 若是成員下還包含其餘能夠收集的成員,須要進一步標記
                                                // 由於引用的層數可能不少致使爆棧,mark_object_simple1會使用mark_stack_array循環標記對象而不是用遞歸
                                                if (contain_pointers_or_collectible (oo))
                                                    mark_object_simple1 (oo, oo THREAD_NUMBER_ARG);
                                            }
                                        }
                    );
            }
        }
    }
}

通過標記階段之後,在堆中存活的對象都被設置了marked標記,若是對象是固定的還會被設置pinned標記
接下來是計劃階段plan_phase:

計劃階段(plan_phase)

在這個階段首先會模擬壓縮和構建Brick Table,在模擬完成後判斷是否應該進行實際的壓縮,
若是進行實際的壓縮則進入重定位階段(relocate_phase)和壓縮階段(compact_phase),不然進入清掃階段(sweep_phase),
在繼續看代碼以前咱們須要先了解計劃階段如何模擬壓縮和什麼是Brick Table。

計劃階段如何模擬壓縮

計劃階段首先會根據相鄰的已標記的對象建立plug,用於加快處理速度和減小須要的內存空間,咱們假定一段內存中的對象以下圖

計劃階段會爲這一段對象建立2個unpinned plug和一個pinned plug:

第一個plug是unpinned plug,包含了對象B, C,不固定地址
第二個plug是pinned plug,包含了對象E, F, G,固定地址
第三個plug是unpinned plug,包含了對象H,不固定地址

各個plug的信息保存在開始地址以前的一段內存中,結構以下

struct plug_and_gap
{
    // 在這個plug以前有多少空間是未被標記(可回收)的
    ptrdiff_t   gap;
    // 壓縮這個plug中的對象時須要移動的偏移值,通常是負數
    ptrdiff_t   reloc;
    union
    {
        // 左邊節點和右邊節點
        pair    m_pair;
        int     lr;  //for clearing the entire pair in one instruction
    };
    // 填充對象(防止覆蓋同步索引塊)
    plug        m_plug;
};

眼尖的會發現上面的圖有兩個問題

  • 對象G不是pinned可是也被歸到pinned plug裏了
    • 這是由於pinned plug會把下一個對象也拉進來防止pinned object的末尾被覆蓋,具體請看下面的代碼
  • 第三個plug把對象G的結尾給覆蓋(破壞)了
    • 對於這種狀況原來的內容會備份到saved_post_plug中,具體請看下面的代碼

多個plug會構建成一棵樹,例如上面的三個plug會構建成這樣的樹:

第一個plug: { gap: 24, reloc: 未定義, m_pair: { left: 0, right: 0 } }
第二個plug: { gap: 132, reloc: 0, m_pair: { left: -356, right: 206 } }
第三個plug: { gap: 24, reloc: 未定義, m_pair: { left: 0, right 0 } }

第二個plug的leftright保存的是離子節點plug的偏移值,
第三個plug的gap比較特殊,可能大家會以爲應該是0可是會被設置爲24(sizeof(gap_reloc_pair)),這個大小在實際複製第二個plug(compact_plug)的時候會加回來。

當計劃階段找到一個plug的開始時,
若是這個plug是pinned plug則加到mark_stack_array隊列中。
當計劃階段找到一個plug的結尾時,
若是這個plug是pinned plug則設置這個plug的大小並移動隊列頂部(mark_stack_tos),
不然使用使用函數allocate_in_condemned_generations計算把這個plug移動到前面(壓縮)時的偏移值,

allocate_in_condemned_generations的原理請看下圖

函數allocate_in_condemned_generations不會實際的移動內存和修改指針,它只設置了plug的reloc成員,
這裏須要注意的是若是有pinned plug而且前面的空間不夠,會從pinned plug的結尾開始計算,
同時出隊列之後的plug Bmark_stack_array中的len會被設置爲前面一段空間的大小,也就是32+39=71

如今讓咱們思考一個問題,若是咱們遇到一個對象x,如何求出對象x應該移動到的位置?

咱們須要根據對象x找到它所在的plug,而後根據這個plug的reloc移動,查找plug使用的索引就是接下來要說的Brick Table

Brick Table

brick_table是一個類型爲short*的數組,用於快速索引plug,如圖

根據所屬的brick不一樣,會構建多個plug樹(避免plug樹過大),而後設置根節點的信息到brick_table中,
brick中的值若是是正值則表示brick對應的開始地址離根節點plug的偏移值+1,
若是是負值則表示plug樹橫跨了多個brick,須要到前面的brick查找。

brick_table相關的代碼以下,咱們能夠看到在64位下brick的大小是4096,在32位下brick的大小是2048

#if defined (_TARGET_AMD64_)
#define brick_size ((size_t)4096)
#else
#define brick_size ((size_t)2048)
#endif //_TARGET_AMD64_

inline
size_t gc_heap::brick_of (uint8_t* add)
{
    return (size_t)(add - lowest_address) / brick_size;
}

inline
uint8_t* gc_heap::brick_address (size_t brick)
{
    return lowest_address + (brick_size * brick);
}

void gc_heap::clear_brick_table (uint8_t* from, uint8_t* end)
{
    for (size_t i = brick_of (from);i < brick_of (end); i++)
        brick_table[i] = 0;
}

//codes for the brick entries:
//entry == 0 -> not assigned
//entry >0 offset is entry-1
//entry <0 jump back entry bricks

inline
void gc_heap::set_brick (size_t index, ptrdiff_t val)
{
    if (val < -32767)
    {
        val = -32767;
    }
    assert (val < 32767);
    if (val >= 0)
        brick_table [index] = (short)val+1;
    else
        brick_table [index] = (short)val;
}

inline
int gc_heap::brick_entry (size_t index)
{
    int val = brick_table [index];
    if (val == 0)
    {
        return -32768;
    }
    else if (val < 0)
    {
        return val;
    }
    else
        return val-1;
}

brick_table中出現負值的狀況是由於plug橫跨幅度比較大,超過了單個brick的時候後面的brick就會設爲負值,
若是對象地址在上圖的1001或1002,查找這個對象對應的plug會從1000的plug樹開始。
另外1002中的值不必定須要是-2,-1也是有效的,若是是-1會一直向前查找直到找到正值的brick。

在上面咱們提到的問題能夠經過brick_table解決,能夠看下面relocate_address函數的代碼。
brick_table在gc過程當中會儲存plug樹,可是在gc完成後(gc不執行時)會儲存各個brick中地址最大的plug,用於給find_first_object等函數定位對象的開始地址使用。

對於Pinned Plug的特殊處理

pinned plug除了會在plug樹和brick table中,還會保存在mark_stack_array隊列中,類型是mark
由於unpinned plugpinned plug相鄰會致使原來的內容被plug信息覆蓋,mark中還會保存如下的特殊信息

  • saved_pre_plug
    • 若是這個pinned plug覆蓋了上一個unpinned plug的結尾,這裏會保存覆蓋前的原始內容
  • saved_pre_plug_reloc
    • 同上,可是這個值用於重定位和壓縮階段(中間會交換)
  • saved_post_plug
    • 若是這個pinned plug被下一個unpinned plug覆蓋告終尾,這裏會保存覆蓋前的原始內容
  • saved_post_plug_reloc
    • 同上,可是這個值用於重定位和壓縮階段(中間會交換)
  • saved_pre_plug_info_reloc_start
    • 被覆蓋的saved_pre_plug內容在重定位後的地址,若是重定位未發生則能夠直接用(first - sizeof (plug_and_gap))
  • saved_post_plug_info_start
    • 被覆蓋的saved_post_plug內容的地址,注意pinned plug不會被重定位
  • saved_pre_p
    • 是否保存了saved_pre_plug
    • 若是覆蓋的內容包含了對象的開頭(對象比較小,整個都被覆蓋了)
    • 這裏還會保存對象離各個引用成員的偏移值的bitmap (enque_pinned_plug)
  • saved_post_p
    • 是否保存了saved_post_p
    • 若是覆蓋的內容包含了對象的開頭(對象比較小,整個都被覆蓋了)
    • 這裏還會保存對象離各個引用成員的偏移值的bitmap (save_post_plug_info)

mark_stack_array中的len意義會在入隊和出隊時有所改變,
入隊時len表明pinned plug的大小,
出隊後len表明pinned plug離最後的模擬壓縮分配地址的空間(這個空間能夠變成free object)。

mark_stack_array

mark_stack_array的結構以下圖:

入隊時mark_stack_tos增長,出隊時mark_stack_bos增長,空間不夠時會擴展而後mark_stack_array_length會增長。

計劃階段判斷使用壓縮(compact)仍是清掃(sweep)的依據是什麼

計劃階段模擬壓縮的時候建立plug,設置reloc等等只是爲了接下來的壓縮作準備,既不會修改指針地址也不會移動內存。
在作完這些工做以後計劃階段會首先判斷應不該該進行壓縮,若是不進行壓縮而是進行清掃,這些計算結果都會浪費掉。

判斷是否使用壓縮的根據主要有

  • 系統空餘空閒是否過少,若是過少觸發swap可能會明顯的拖低性能,這時候應該嘗試壓縮
  • 碎片空間大小(fragmentation) >= 閾值(dd_fragmentation_limit)
  • 碎片空間大小(fragmentation) / 收集代的大小(包括更年輕的代) >= 閾值(dd_fragmentation_burden_limit)

其餘還有一些零碎的判斷,將在下面的decide_on_compacting函數的代碼中講解。

對象的升代與降代

在不少介紹.Net GC的書籍中都有提到過,通過GC之後對象會升代,例如gen 0中的對象在一次GC後若是存活下來會變爲gen 1。
在CoreCLR中,對象的升代須要知足必定條件,某些特殊狀況下不會升代,甚至會降代(gen1變爲gen0)。
對象升代的條件以下:

  • 計劃階段(plan_phase)選擇清掃(sweep)時會啓用升代
  • 入口點(garbage_collect)判斷當前是Full GC時會啓用升代
  • dt_low_card_table_efficiency_p成立時會啓用升代
    • 請在前面查找dt_low_card_table_efficiency_p查看該處的解釋
  • 計劃階段(plan_phase)判斷上一代太小,或者此次標記(存活)的對象過多時啓用升代
    • 請在後面查找promoted_bytes (i) > m查看該處的解釋

若是升代的條件不知足,則原來在gen 0的對象GC後仍然會在gen 0,
某些特殊條件下還會發生降代,以下圖:

在模擬壓縮時,原來在gen 1的對象會歸到gen 2(pinned object不必定),原來在gen 0的對象會歸到gen 1,
可是若是全部unpinned plug都已經壓縮到前面,後面還有殘留的pinned plug時,後面殘留的pinned plug中的對象則會不升代或者降代,
當這種狀況發生時計劃階段會設置demotion_low來標記被降代的範圍。
若是最終選擇了清掃(sweep)則上圖中的狀況不會發生。

計劃代邊界

計劃階段在模擬壓縮的時候還會計劃代邊界(generation::plan_allocation_start),
計劃代邊界的工做主要在process_ephemeral_boundaries, plan_generation_start, plan_generation_starts函數中完成。
大部分狀況下函數process_ephemeral_boundaries會用來計劃gen 1的邊界,若是不升代這個函數還會計劃gen 0的邊界,
當判斷當前計劃的plug大於或等於下一代的邊界時,例如大於等於gen 0的邊界時則會設置gen 1的邊界在這個plug的前面。

最終選擇壓縮(compact)時,會把新的代邊界設置成計劃代邊界(請看fix_generation_bounds函數),
最終選擇清掃(sweep)時,計劃代邊界不會被使用(請看make_free_lists函數和make_free_list_in_brick函數)。

計劃階段(plan_phase)的代碼

gc_heap::plan_phase函數的代碼以下

void gc_heap::plan_phase (int condemned_gen_number)
{
    // 若是收集代是gen 1則記錄原來gen 2的大小
    size_t old_gen2_allocated = 0;
    size_t old_gen2_size = 0;

    if (condemned_gen_number == (max_generation - 1))
    {
        old_gen2_allocated = generation_free_list_allocated (generation_of (max_generation));
        old_gen2_size = generation_size (max_generation);
    }

    assert (settings.concurrent == FALSE);

    // 統計計劃階段的開始時間
    // %type%  category = quote (plan);
#ifdef TIME_GC
    unsigned start;
    unsigned finish;
    start = GetCycleCount32();
#endif //TIME_GC

    dprintf (2,("---- Plan Phase ---- Condemned generation %d, promotion: %d",
                condemned_gen_number, settings.promotion ? 1 : 0));

    // 收集代的對象
    generation*  condemned_gen1 = generation_of (condemned_gen_number);

    // 判斷以前是否使用了mark list
    // 標記對象較少時用mark list能夠提高速度
#ifdef MARK_LIST
    BOOL use_mark_list = FALSE;
    uint8_t** mark_list_next = &mark_list[0];
#ifdef GC_CONFIG_DRIVEN
    dprintf (3, ("total number of marked objects: %Id (%Id)",
                 (mark_list_index - &mark_list[0]), ((mark_list_end - &mark_list[0]))));
#else
    dprintf (3, ("mark_list length: %Id",
                 (mark_list_index - &mark_list[0])));
#endif //GC_CONFIG_DRIVEN

    if ((condemned_gen_number < max_generation) &&
        (mark_list_index <= mark_list_end) 
#ifdef BACKGROUND_GC        
        && (!recursive_gc_sync::background_running_p())
#endif //BACKGROUND_GC
        )
    {
#ifndef MULTIPLE_HEAPS
        _sort (&mark_list[0], mark_list_index-1, 0);
        //printf ("using mark list at GC #%d", dd_collection_count (dynamic_data_of (0)));
        //verify_qsort_array (&mark_list[0], mark_list_index-1);
#endif //!MULTIPLE_HEAPS
        use_mark_list = TRUE;
        get_gc_data_per_heap()->set_mechanism_bit (gc_mark_list_bit);
    }
    else
    {
        dprintf (3, ("mark_list not used"));
    }

#endif //MARK_LIST

    // 清除read only segment中的marked bit
#ifdef FEATURE_BASICFREEZE
    if ((generation_start_segment (condemned_gen1) != ephemeral_heap_segment) &&
        ro_segments_in_range)
    {
        sweep_ro_segments (generation_start_segment (condemned_gen1));
    }
#endif // FEATURE_BASICFREEZE

    // 根據以前使用m_boundary記錄的slow和shigh快速清掃slow前面和shigh後面的垃圾對象
    // shigh等於0表示無對象存活
    // if (shigh != (uint8_t*)0)
    //    對於slow, 調用make_unused_array
    //    對於shigh, 設置heap_segment_allocated
    //    對於範圍外的segment, heap_segment_allocated (seg) = heap_segment_mem (seg); // 整個segment都被清空,後面可刪除
    // else
    //    第一個segment, heap_segment_allocated (seg) = generation_allocation_start (condemned_gen1);
    //    後面的segment, heap_segment_allocated (seg) =  heap_segment_mem (seg); // 整個segment都被清空,後面可刪除
#ifndef MULTIPLE_HEAPS
    if (shigh != (uint8_t*)0)
    {
        heap_segment* seg = heap_segment_rw (generation_start_segment (condemned_gen1));

        PREFIX_ASSUME(seg != NULL);

        heap_segment* fseg = seg;
        do
        {
            if (slow > heap_segment_mem (seg) &&
                slow < heap_segment_reserved (seg))
            {
                if (seg == fseg)
                {
                    uint8_t* o = generation_allocation_start (condemned_gen1) +
                        Align (size (generation_allocation_start (condemned_gen1)));
                    if (slow > o)
                    {
                        assert ((slow - o) >= (int)Align (min_obj_size));
#ifdef BACKGROUND_GC
                        if (current_c_gc_state == c_gc_state_marking)
                        {
                            bgc_clear_batch_mark_array_bits (o, slow);
                        }
#endif //BACKGROUND_GC
                        make_unused_array (o, slow - o);
                    }
                } 
                else
                {
                    assert (condemned_gen_number == max_generation);
                    make_unused_array (heap_segment_mem (seg),
                                       slow - heap_segment_mem (seg));
                }
            }
            if (in_range_for_segment (shigh, seg))
            {
#ifdef BACKGROUND_GC
                if (current_c_gc_state == c_gc_state_marking)
                {
                    bgc_clear_batch_mark_array_bits ((shigh + Align (size (shigh))), heap_segment_allocated (seg));
                }
#endif //BACKGROUND_GC
                heap_segment_allocated (seg) = shigh + Align (size (shigh));
            }
            // test if the segment is in the range of [slow, shigh]
            if (!((heap_segment_reserved (seg) >= slow) &&
                  (heap_segment_mem (seg) <= shigh)))
            {
                // shorten it to minimum
                heap_segment_allocated (seg) =  heap_segment_mem (seg);
            }
            seg = heap_segment_next_rw (seg);
        } while (seg);
    }
    else
    {
        heap_segment* seg = heap_segment_rw (generation_start_segment (condemned_gen1));

        PREFIX_ASSUME(seg != NULL);

        heap_segment* sseg = seg;
        do
        {
            // shorten it to minimum
            if (seg == sseg)
            {
                // no survivors make all generations look empty
                uint8_t* o = generation_allocation_start (condemned_gen1) +
                    Align (size (generation_allocation_start (condemned_gen1)));
#ifdef BACKGROUND_GC
                if (current_c_gc_state == c_gc_state_marking)
                {
                    bgc_clear_batch_mark_array_bits (o, heap_segment_allocated (seg));
                }
#endif //BACKGROUND_GC
                heap_segment_allocated (seg) = o;
            }
            else
            {
                assert (condemned_gen_number == max_generation);
#ifdef BACKGROUND_GC
                if (current_c_gc_state == c_gc_state_marking)
                {
                    bgc_clear_batch_mark_array_bits (heap_segment_mem (seg), heap_segment_allocated (seg));
                }
#endif //BACKGROUND_GC
                heap_segment_allocated (seg) =  heap_segment_mem (seg);
            }
            seg = heap_segment_next_rw (seg);
        } while (seg);
    }

#endif //MULTIPLE_HEAPS

    // 當前計劃的segment,會隨着計劃向後移動
    heap_segment*  seg1 = heap_segment_rw (generation_start_segment (condemned_gen1));

    PREFIX_ASSUME(seg1 != NULL);

    // 當前計劃的segment的結束地址
    uint8_t*  end = heap_segment_allocated (seg1);
    // 收集代的第一個對象(地址)
    uint8_t*  first_condemned_address = generation_allocation_start (condemned_gen1);
    // 當前計劃的對象
    uint8_t*  x = first_condemned_address;

    assert (!marked (x));
    // 當前plug的結束地址
    uint8_t*  plug_end = x;
    // 當前plug樹的根節點
    uint8_t*  tree = 0;
    // 構建plug樹使用的序列
    size_t  sequence_number = 0;
    // 上一次的plug節點
    uint8_t*  last_node = 0;
    // 當前計劃的brick
    size_t  current_brick = brick_of (x);
    // 是否從計劃代開始模擬分配(這個變量後面還會設爲true)
    BOOL  allocate_in_condemned = ((condemned_gen_number == max_generation)||
                                   (settings.promotion == FALSE));
    // 當前計劃的舊代和新代,這兩個變量用於從新決定代邊界(generation_allocation_start)
    int  active_old_gen_number = condemned_gen_number;
    int  active_new_gen_number = (allocate_in_condemned ? condemned_gen_number:
                                  (1 + condemned_gen_number));
    // 收集代的上一代(若是收集代是gen 2這裏會設爲gen 2)
    generation*  older_gen = 0;
    // 模擬分配的代
    generation* consing_gen = condemned_gen1;

    // older_gen的原始數據備份
    alloc_list  r_free_list [MAX_BUCKET_COUNT];
    size_t r_free_list_space = 0;
    size_t r_free_obj_space = 0;
    size_t r_older_gen_free_list_allocated = 0;
    size_t r_older_gen_condemned_allocated = 0;
    size_t r_older_gen_end_seg_allocated = 0;
    uint8_t*  r_allocation_pointer = 0;
    uint8_t*  r_allocation_limit = 0;
    uint8_t* r_allocation_start_region = 0;
    heap_segment*  r_allocation_segment = 0;
#ifdef FREE_USAGE_STATS
    size_t r_older_gen_free_space[NUM_GEN_POWER2];
#endif //FREE_USAGE_STATS

    // 在計劃以前備份older_gen的數據
    if ((condemned_gen_number < max_generation))
    {
        older_gen = generation_of (min (max_generation, 1 + condemned_gen_number));
        generation_allocator (older_gen)->copy_to_alloc_list (r_free_list);

        r_free_list_space = generation_free_list_space (older_gen);
        r_free_obj_space = generation_free_obj_space (older_gen);
#ifdef FREE_USAGE_STATS
        memcpy (r_older_gen_free_space, older_gen->gen_free_spaces, sizeof (r_older_gen_free_space));
#endif //FREE_USAGE_STATS
        generation_allocate_end_seg_p (older_gen) = FALSE;
        r_older_gen_free_list_allocated = generation_free_list_allocated (older_gen);
        r_older_gen_condemned_allocated = generation_condemned_allocated (older_gen);
        r_older_gen_end_seg_allocated = generation_end_seg_allocated (older_gen);
        r_allocation_limit = generation_allocation_limit (older_gen);
        r_allocation_pointer = generation_allocation_pointer (older_gen);
        r_allocation_start_region = generation_allocation_context_start_region (older_gen);
        r_allocation_segment = generation_allocation_segment (older_gen);
        heap_segment* start_seg = heap_segment_rw (generation_start_segment (older_gen));

        PREFIX_ASSUME(start_seg != NULL);

        if (start_seg != ephemeral_heap_segment)
        {
            assert (condemned_gen_number == (max_generation - 1));
            while (start_seg && (start_seg != ephemeral_heap_segment))
            {
                assert (heap_segment_allocated (start_seg) >=
                        heap_segment_mem (start_seg));
                assert (heap_segment_allocated (start_seg) <=
                        heap_segment_reserved (start_seg));
                heap_segment_plan_allocated (start_seg) =
                    heap_segment_allocated (start_seg);
                start_seg = heap_segment_next_rw (start_seg);
            }
        }
    }

    // 重設收集代之後的的全部segment的plan_allocated(計劃分配的對象大小合計)
    //reset all of the segment allocated sizes
    {
        heap_segment*  seg2 = heap_segment_rw (generation_start_segment (condemned_gen1));

        PREFIX_ASSUME(seg2 != NULL);

        while (seg2)
        {
            heap_segment_plan_allocated (seg2) =
                heap_segment_mem (seg2);
            seg2 = heap_segment_next_rw (seg2);
        }
    }

    // 重設gen 0 ~ 收集代的數據
    int  condemned_gn = condemned_gen_number;

    int bottom_gen = 0;
    init_free_and_plug();

    while (condemned_gn >= bottom_gen)
    {
        generation*  condemned_gen2 = generation_of (condemned_gn);
        generation_allocator (condemned_gen2)->clear();
        generation_free_list_space (condemned_gen2) = 0;
        generation_free_obj_space (condemned_gen2) = 0;
        generation_allocation_size (condemned_gen2) = 0;
        generation_condemned_allocated (condemned_gen2) = 0; 
        generation_pinned_allocated (condemned_gen2) = 0; 
        generation_free_list_allocated(condemned_gen2) = 0; 
        generation_end_seg_allocated (condemned_gen2) = 0; 
        // 執行清掃(sweep)時對應代增長的固定對象(pinned object)大小
        generation_pinned_allocation_sweep_size (condemned_gen2) = 0;
        // 執行壓縮(compact)時對應代增長的固定對象(pinned object)大小
        generation_pinned_allocation_compact_size (condemned_gen2) = 0;
#ifdef FREE_USAGE_STATS
        generation_pinned_free_obj_space (condemned_gen2) = 0;
        generation_allocated_in_pinned_free (condemned_gen2) = 0;
        generation_allocated_since_last_pin (condemned_gen2) = 0;
#endif //FREE_USAGE_STATS
        // 計劃的代邊界
        generation_plan_allocation_start (condemned_gen2) = 0;
        generation_allocation_segment (condemned_gen2) =
            heap_segment_rw (generation_start_segment (condemned_gen2));

        PREFIX_ASSUME(generation_allocation_segment(condemned_gen2) != NULL);

        // 設置分配上下文地址,模擬壓縮時使用
        if (generation_start_segment (condemned_gen2) != ephemeral_heap_segment)
        {
            generation_allocation_pointer (condemned_gen2) =
                heap_segment_mem (generation_allocation_segment (condemned_gen2));
        }
        else
        {
            generation_allocation_pointer (condemned_gen2) = generation_allocation_start (condemned_gen2);
        }

        generation_allocation_limit (condemned_gen2) = generation_allocation_pointer (condemned_gen2);
        generation_allocation_context_start_region (condemned_gen2) = generation_allocation_pointer (condemned_gen2);

        condemned_gn--;
    }

    // 在處理全部對象以前是否要先決定一個代的邊界
    // 不升代或者收集代是gen 2(Full GC)時須要
    BOOL allocate_first_generation_start = FALSE;
    
    if (allocate_in_condemned)
    {
        allocate_first_generation_start = TRUE;
    }

    dprintf(3,( " From %Ix to %Ix", (size_t)x, (size_t)end));

    // 記錄對象降代(原來gen 1的對象變爲gen 0)的狀況
    // 關於不升代和降代的條件和處理將在下面解釋
    demotion_low = MAX_PTR;
    demotion_high = heap_segment_allocated (ephemeral_heap_segment);

    // 判斷是否應該阻止gen 1中的固定對象降代
    // 若是隻是收集緣由只是由於dt_low_card_table_efficiency_p則須要阻止降代
    // demote_gen1_p = false時會在下面調用advance_pins_for_demotion函數
    // If we are doing a gen1 only because of cards, it means we should not demote any pinned plugs
    // from gen1. They should get promoted to gen2.
    demote_gen1_p = !(settings.promotion && 
                      (settings.condemned_generation == (max_generation - 1)) && 
                      gen_to_condemn_reasons.is_only_condition (gen_low_card_p));

    total_ephemeral_size = 0;

    // 打印除錯信息
    print_free_and_plug ("BP");

    // 打印除錯信息
    for (int gen_idx = 0; gen_idx <= max_generation; gen_idx++)
    {
        generation* temp_gen = generation_of (gen_idx);

        dprintf (2, ("gen%d start %Ix, plan start %Ix",
            gen_idx, 
            generation_allocation_start (temp_gen),
            generation_plan_allocation_start (temp_gen)));
    }

    // 觸發etw時間
    BOOL fire_pinned_plug_events_p = ETW_EVENT_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PRIVATE_PROVIDER_Context, PinPlugAtGCTime);
    size_t last_plug_len = 0;

    // 開始模擬壓縮
    // 會建立plug,設置brick table和模擬plug的移動
    while (1)
    {
        // 應該處理下個segment
        if (x >= end)
        {
            assert (x == end);
            assert (heap_segment_allocated (seg1) == end);
            heap_segment_allocated (seg1) = plug_end;

            // 設置brick table
            current_brick = update_brick_table (tree, current_brick, x, plug_end);
            dprintf (3, ("end of seg: new tree, sequence# 0"));
            sequence_number = 0;
            tree = 0;

            // 有下一個segment,繼續處理
            if (heap_segment_next_rw (seg1))
            {
                seg1 = heap_segment_next_rw (seg1);
                end = heap_segment_allocated (seg1);
                plug_end = x = heap_segment_mem (seg1);
                current_brick = brick_of (x);
                dprintf(3,( " From %Ix to %Ix", (size_t)x, (size_t)end));
                continue;
            }
            // 無下一個segment,跳出模擬壓縮的循環
            else
            {
                break;
            }
        }

        // 上一個plug是否unpinned plug
        BOOL last_npinned_plug_p = FALSE;
        // 上一個plug是否pinned plug
        BOOL last_pinned_plug_p = FALSE;

        // 上一個pinned plug的地址,合併pinned plug時使用
        // last_pinned_plug is the beginning of the last pinned plug. If we merge a plug into a pinned
        // plug we do not change the value of last_pinned_plug. This happens with artificially pinned plugs -
        // it can be merged with a previous pinned plug and a pinned plug after it can be merged with it.
        uint8_t* last_pinned_plug = 0;
        size_t num_pinned_plugs_in_plug = 0;

        // 當前plug的最後一個對象的地址
        uint8_t* last_object_in_plug = 0;

        // 枚舉segment中的對象,若是第一個對象未被標記不會進入如下的處理
        while ((x < end) && marked (x))
        {
            // 記錄plug的開始
            uint8_t*  plug_start = x;
            uint8_t*  saved_plug_end = plug_end;
            // 當前plug中的對象是否pinned object
            // 會輪流切換
            BOOL   pinned_plug_p = FALSE;
            BOOL   npin_before_pin_p = FALSE;
            BOOL   saved_last_npinned_plug_p = last_npinned_plug_p;
            uint8_t*  saved_last_object_in_plug = last_object_in_plug;
            BOOL   merge_with_last_pin_p = FALSE;

            size_t added_pinning_size = 0;
            size_t artificial_pinned_size = 0;

            // 預先保存一部分plug信息
            // 設置這個plug和上一個plug的結尾之間的gap
            // 若是當前plug是pinned plug
            // - 調用enque_pinned_plug把plug信息保存到mark_stack_array隊列
            //   - enque_pinned_plug不會設置長度(len)和移動隊列頂部(mark_stack_tos),這部分工做會在set_pinned_info完成
            //   - 檢測當前pinned plug是否覆蓋了前一個unpinned plug的結尾
            //   - 若是覆蓋了須要把原來的內容複製到saved_pre_plug和saved_pre_plug_reloc (函數enque_pinned_plug)
            // 若是當前plug是unpinned plug
            //   - 檢測當前unpinned plug是否覆蓋了前一個pinned plug的結尾
            //   - 若是覆蓋了須要把原來的內容複製到saved_post_plug和saved_post_plug_reloc (函數save_post_plug_info)
            store_plug_gap_info (plug_start, plug_end, last_npinned_plug_p, last_pinned_plug_p, 
                                 last_pinned_plug, pinned_plug_p, last_object_in_plug, 
                                 merge_with_last_pin_p, last_plug_len);

#ifdef FEATURE_STRUCTALIGN
            int requiredAlignment = ((CObjectHeader*)plug_start)->GetRequiredAlignment();
            size_t alignmentOffset = OBJECT_ALIGNMENT_OFFSET;
#endif // FEATURE_STRUCTALIGN

            {
                // 枚舉接下來的對象,若是對象未被標記,或者對象是否固定和pinned_plug_p不一致則中斷
                // 這裏枚舉到的對象都會歸到同一個plug裏面
                uint8_t* xl = x;
                while ((xl < end) && marked (xl) && (pinned (xl) == pinned_plug_p))
                {
                    assert (xl < end);
                    // 清除pinned bit
                    // 像前面所說的,GC裏面marked和pinned標記都是臨時使用的,在計劃階段會被清除
                    if (pinned(xl))
                    {
                        clear_pinned (xl);
                    }
#ifdef FEATURE_STRUCTALIGN
                    else
                    {
                        int obj_requiredAlignment = ((CObjectHeader*)xl)->GetRequiredAlignment();
                        if (obj_requiredAlignment > requiredAlignment)
                        {
                            requiredAlignment = obj_requiredAlignment;
                            alignmentOffset = xl - plug_start + OBJECT_ALIGNMENT_OFFSET;
                        }
                    }
#endif // FEATURE_STRUCTALIGN

                    // 清除marked bit
                    clear_marked (xl);

                    dprintf(4, ("+%Ix+", (size_t)xl));
                    assert ((size (xl) > 0));
                    assert ((size (xl) <= LARGE_OBJECT_SIZE));

                    // 記錄當前plug的最後一個對象
                    last_object_in_plug = xl;

                    // 下一個對象
                    xl = xl + Align (size (xl));
                    Prefetch (xl);
                }

                BOOL next_object_marked_p = ((xl < end) && marked (xl));

                // 若是當前plug是pinned plug但下一個不是,表明當前plug的結尾須要被覆蓋掉作下一個plug的信息
                // 咱們不想動pinned plug的內容,因此這裏須要犧牲下一個對象,把下一個對象拉到這個plug裏面
                if (pinned_plug_p)
                {
                    // If it is pinned we need to extend to the next marked object as we can't use part of
                    // a pinned object to make the artificial gap (unless the last 3 ptr sized words are all
                    // references but for now I am just using the next non pinned object for that).
                    if (next_object_marked_p) 
                    {
                        clear_marked (xl);
                        last_object_in_plug = xl;
                        size_t extra_size = Align (size (xl));
                        xl = xl + extra_size;
                        added_pinning_size = extra_size;
                    }
                }
                else
                {
                    // 當前plug是unpinned plug,下一個plug是pinned plug
                    if (next_object_marked_p)
                        npin_before_pin_p = TRUE;
                }

                assert (xl <= end);
                x = xl;
            }
            dprintf (3, ( "%Ix[", (size_t)x));
            // 設置plug的結尾
            plug_end = x;
            // plug大小 = 結尾 - 開頭
            size_t ps = plug_end - plug_start;
            last_plug_len = ps;
            dprintf (3, ( "%Ix[(%Ix)", (size_t)x, ps));
            uint8_t*  new_address = 0;

            // 有時候若是一個unpinned plug很大,咱們想人工固定它(artificially pinned plug)
            // 若是前一個plug也是pinned plug則和前一個plug整合到一個,不然進入mark_stack_array隊列中
            if (!pinned_plug_p)
            {
                if (allocate_in_condemned &&
                    (settings.condemned_generation == max_generation) &&
                    (ps > (OS_PAGE_SIZE)))
                {
                    ptrdiff_t reloc = plug_start - generation_allocation_pointer (consing_gen);
                    //reloc should >=0 except when we relocate
                    //across segments and the dest seg is higher then the src

                    if ((ps > (8*OS_PAGE_SIZE)) &&
                        (reloc > 0) &&
                        ((size_t)reloc < (ps/16)))
                    {
                        dprintf (3, ("Pinning %Ix; reloc would have been: %Ix",
                                     (size_t)plug_start, reloc));
                        // The last plug couldn't have been a npinned plug or it would have
                        // included this plug.
                        assert (!saved_last_npinned_plug_p);

                        if (last_pinned_plug)
                        {
                            dprintf (3, ("artificially pinned plug merged with last pinned plug"));
                            merge_with_last_pin_p = TRUE;
                        }
                        else
                        {
                            enque_pinned_plug (plug_start, FALSE, 0);
                            last_pinned_plug = plug_start;
                        }

                        convert_to_pinned_plug (last_npinned_plug_p, last_pinned_plug_p, pinned_plug_p,
                                                ps, artificial_pinned_size);
                    }
                }
            }

            // 若是在作Full GC或者不升代,決定第一個代的邊界
            // plan_generation_start用於計劃代的邊界(generation_plan_generation_start)
            // Full GC時gen 2的邊界會在這裏決定
            if (allocate_first_generation_start)
            {
                allocate_first_generation_start = FALSE;
                plan_generation_start (condemned_gen1, consing_gen, plug_start);
                assert (generation_plan_allocation_start (condemned_gen1));
            }

            // 若是模擬的segment是ephemeral heap segment
            // 在這裏決定gen 1的邊界
            // 若是不升代這裏也會決定gen 0的邊界
            if (seg1 == ephemeral_heap_segment)
            {
                process_ephemeral_boundaries (plug_start, active_new_gen_number,
                                              active_old_gen_number,
                                              consing_gen,
                                              allocate_in_condemned);
            }

            dprintf (3, ("adding %Id to gen%d surv", ps, active_old_gen_number));

            // 統計存活的對象大小
            dynamic_data* dd_active_old = dynamic_data_of (active_old_gen_number);
            dd_survived_size (dd_active_old) += ps;

            // 模擬壓縮的時候有可能會要求把當前unpinned plug轉換爲pinned plug
            BOOL convert_to_pinned_p = FALSE;

            // 若是plug是unpinned plug,模擬壓縮
            if (!pinned_plug_p)
            {
#if defined (RESPECT_LARGE_ALIGNMENT) || defined (FEATURE_STRUCTALIGN)
                dd_num_npinned_plugs (dd_active_old)++;
#endif //RESPECT_LARGE_ALIGNMENT || FEATURE_STRUCTALIGN

                // 更新統計信息
                add_gen_plug (active_old_gen_number, ps);

                if (allocate_in_condemned)
                {
                    verify_pins_with_post_plug_info("before aic");

                    // 在收集代分配,必要時跳過pinned plug,返回新的地址
                    new_address =
                        allocate_in_condemned_generations (consing_gen,
                                                           ps,
                                                           active_old_gen_number,
#ifdef SHORT_PLUGS
                                                           &convert_to_pinned_p,
                                                           (npin_before_pin_p ? plug_end : 0),
                                                           seg1,
#endif //SHORT_PLUGS
                                                           plug_start REQD_ALIGN_AND_OFFSET_ARG);
                    verify_pins_with_post_plug_info("after aic");
                }
                else
                {
                    // 在上一代分配,必要時跳過pinned plug,返回新的地址
                    new_address = allocate_in_older_generation (older_gen, ps, active_old_gen_number, plug_start REQD_ALIGN_AND_OFFSET_ARG);

                    if (new_address != 0)
                    {
                        if (settings.condemned_generation == (max_generation - 1))
                        {
                            dprintf (3, (" NA: %Ix-%Ix -> %Ix, %Ix (%Ix)",
                                plug_start, plug_end,
                                (size_t)new_address, (size_t)new_address + (plug_end - plug_start),
                                (size_t)(plug_end - plug_start)));
                        }
                    }
                    else
                    {
                        // 失敗時(空間不足)改成在收集代分配
                        allocate_in_condemned = TRUE;

                        new_address = allocate_in_condemned_generations (consing_gen, ps, active_old_gen_number, 
#ifdef SHORT_PLUGS
                                                                         &convert_to_pinned_p,
                                                                         (npin_before_pin_p ? plug_end : 0),
                                                                         seg1,
#endif //SHORT_PLUGS
                                                                         plug_start REQD_ALIGN_AND_OFFSET_ARG);
                    }
                }

                // 若是要求把當前unpinned plug轉換爲pinned plug
                if (convert_to_pinned_p)
                {
                    assert (last_npinned_plug_p != FALSE);
                    assert (last_pinned_plug_p == FALSE);
                    convert_to_pinned_plug (last_npinned_plug_p, last_pinned_plug_p, pinned_plug_p,
                                            ps, artificial_pinned_size);
                    enque_pinned_plug (plug_start, FALSE, 0);
                    last_pinned_plug = plug_start;
                }
                else
                {
                    // 找不到空間(不移動這個plug)時驗證是在ephemeral heap segment的末尾
                    // 這裏還不會設置reloc,到下面的set_node_relocation_distance纔會設
                    if (!new_address)
                    {
                        //verify that we are at then end of the ephemeral segment
                        assert (generation_allocation_segment (consing_gen) ==
                                ephemeral_heap_segment);
                        //verify that we are near the end
                        assert ((generation_allocation_pointer (consing_gen) + Align (ps)) <
                                heap_segment_allocated (ephemeral_heap_segment));
                        assert ((generation_allocation_pointer (consing_gen) + Align (ps)) >
                                (heap_segment_allocated (ephemeral_heap_segment) + Align (min_obj_size)));
                    }
                    else
                    {
#ifdef SIMPLE_DPRINTF
                        dprintf (3, ("(%Ix)[%Ix->%Ix, NA: [%Ix(%Id), %Ix[: %Ix(%d)",
                            (size_t)(node_gap_size (plug_start)), 
                            plug_start, plug_end, (size_t)new_address, (size_t)(plug_start - new_address),
                                (size_t)new_address + ps, ps, 
                                (is_plug_padded (plug_start) ? 1 : 0)));
#endif //SIMPLE_DPRINTF

#ifdef SHORT_PLUGS
                        if (is_plug_padded (plug_start))
                        {
                            dprintf (3, ("%Ix was padded", plug_start));
                            dd_padding_size (dd_active_old) += Align (min_obj_size);
                        }
#endif //SHORT_PLUGS
                    }
                }
            }

            // 若是當前plug是pinned plug
            if (pinned_plug_p)
            {
                if (fire_pinned_plug_events_p)
                    FireEtwPinPlugAtGCTime(plug_start, plug_end, 
                                           (merge_with_last_pin_p ? 0 : (uint8_t*)node_gap_size (plug_start)),
                                           GetClrInstanceId());

                // 和上一個pinned plug合併
                if (merge_with_last_pin_p)
                {
                    merge_with_last_pinned_plug (last_pinned_plug, ps);
                }
                // 設置隊列中的pinned plug大小(len)並移動隊列頂部(mark_stack_tos++)
                else
                {
                    assert (last_pinned_plug == plug_start);
                    set_pinned_info (plug_start, ps, consing_gen);
                }

                // pinned plug不能移動,新地址和原地址同樣
                new_address = plug_start;

                dprintf (3, ( "(%Ix)PP: [%Ix, %Ix[%Ix](m:%d)",
                            (size_t)(node_gap_size (plug_start)), (size_t)plug_start,
                            (size_t)plug_end, ps,
                            (merge_with_last_pin_p ? 1 : 0)));

                // 統計存活對象的大小,固定對象的大小和人工固定對象的大小
                dprintf (3, ("adding %Id to gen%d pinned surv", plug_end - plug_start, active_old_gen_number));
                dd_pinned_survived_size (dd_active_old) += plug_end - plug_start;
                dd_added_pinned_size (dd_active_old) += added_pinning_size;
                dd_artificial_pinned_survived_size (dd_active_old) += artificial_pinned_size;

                // 若是須要禁止降代gen 1的對象,記錄在gen 1中最後一個pinned plug的結尾
                if (!demote_gen1_p && (active_old_gen_number == (max_generation - 1)))
                {
                    last_gen1_pin_end = plug_end;
                }
            }

#ifdef _DEBUG
            // detect forward allocation in the same segment
            assert (!((new_address > plug_start) &&
                (new_address < heap_segment_reserved (seg1))));
#endif //_DEBUG

            // 若是不合併到上一個pinned plug
            // 在這裏能夠設置偏移值(reloc)和更新brick table了
            if (!merge_with_last_pin_p)
            {
                // 若是已經在下一個brick
                // 把以前的plug樹設置到以前的brick中,並重設plug樹
                // 若是以前的plug跨了多個brick,update_brick_table會設置後面的brick爲-1
                if (current_brick != brick_of (plug_start))
                {
                    current_brick = update_brick_table (tree, current_brick, plug_start, saved_plug_end);
                    sequence_number = 0;
                    tree = 0;
                }

                // 更新plug的偏移值(reloc)
                // 這裏的偏移值會用在後面的重定位階段(relocate_phase)和壓縮階段(compact_phase)
                set_node_relocation_distance (plug_start, (new_address - plug_start));

                // 構建plug樹
                if (last_node && (node_relocation_distance (last_node) ==
                                  (node_relocation_distance (plug_start) +
                                   (int)node_gap_size (plug_start))))
                {
                    //dprintf(3,( " Lb"));
                    dprintf (3, ("%Ix Lb", plug_start));
                    set_node_left (plug_start);
                }
                if (0 == sequence_number)
                {
                    dprintf (2, ("sn: 0, tree is set to %Ix", plug_start));
                    tree = plug_start;
                }

                verify_pins_with_post_plug_info("before insert node");

                tree = insert_node (plug_start, ++sequence_number, tree, last_node);
                dprintf (3, ("tree is %Ix (b: %Ix) after insert_node", tree, brick_of (tree)));
                last_node = plug_start;

                // 這個處理只用於除錯
                // 若是這個plug是unpinned plug而且覆蓋了上一個pinned plug的結尾
                // 把覆蓋的內容複製到pinned plug關聯的saved_post_plug_debug
#ifdef _DEBUG
                // If we detect if the last plug is pinned plug right before us, we should save this gap info
                if (!pinned_plug_p)
                {
                    if (mark_stack_tos > 0)
                    {
                        mark& m = mark_stack_array[mark_stack_tos - 1];
                        if (m.has_post_plug_info())
                        {
                            uint8_t* post_plug_info_start = m.saved_post_plug_info_start;
                            size_t* current_plug_gap_start = (size_t*)(plug_start - sizeof (plug_and_gap));
                            if ((uint8_t*)current_plug_gap_start == post_plug_info_start)
                            {
                                dprintf (3, ("Ginfo: %Ix, %Ix, %Ix",
                                    *current_plug_gap_start, *(current_plug_gap_start + 1),
                                    *(current_plug_gap_start + 2)));
                                memcpy (&(m.saved_post_plug_debug), current_plug_gap_start, sizeof (gap_reloc_pair));
                            }
                        }
                    }
                }
#endif //_DEBUG

                verify_pins_with_post_plug_info("after insert node");
            }
        }
        
        if (num_pinned_plugs_in_plug > 1)
        {
            dprintf (3, ("more than %Id pinned plugs in this plug", num_pinned_plugs_in_plug));
        }

        // 跳過未標記的對象找到下一個已標記的對象
        // 若是有mark_list能夠加快找到下一個已標記對象的速度
        {
#ifdef MARK_LIST
            if (use_mark_list)
            {
               while ((mark_list_next < mark_list_index) &&
                      (*mark_list_next <= x))
               {
                   mark_list_next++;
               }
               if ((mark_list_next < mark_list_index)
#ifdef MULTIPLE_HEAPS
                   && (*mark_list_next < end) //for multiple segments
#endif //MULTIPLE_HEAPS
                   )
                   x = *mark_list_next;
               else
                   x = end;
            }
            else
#endif //MARK_LIST
            {
                uint8_t* xl = x;
#ifdef BACKGROUND_GC
                if (current_c_gc_state == c_gc_state_marking)
                {
                    assert (recursive_gc_sync::background_running_p());
                    while ((xl < end) && !marked (xl))
                    {
                        dprintf (4, ("-%Ix-", (size_t)xl));
                        assert ((size (xl) > 0));
                        background_object_marked (xl, TRUE);
                        xl = xl + Align (size (xl));
                        Prefetch (xl);
                    }
                }
                else
#endif //BACKGROUND_GC
                {
                    // 跳過未標記的對象
                    while ((xl < end) && !marked (xl))
                    {
                        dprintf (4, ("-%Ix-", (size_t)xl));
                        assert ((size (xl) > 0));
                        xl = xl + Align (size (xl));
                        Prefetch (xl);
                    }
                }
                assert (xl <= end);
                // 找到了下一個已標記的對象,或者當前segment中的對象已經搜索完畢
                x = xl;
            }
        }
    }

    // 處理mark_stack_array中還沒有出隊的pinned plug
    // 這些plug已經在全部已壓縮的unpinned plug後面,咱們能夠把這些pinned plug降級(降到gen 0),也能夠防止它們降級
    while (!pinned_plug_que_empty_p())
    {
        // 計算代邊界和處理降代
        // 不在ephemeral heap segment的pinned plug不會被降代
        // 前面調用的process_ephemeral_boundaries中有相同的處理
        if (settings.promotion)
        {
            uint8_t* pplug = pinned_plug (oldest_pin());
            if (in_range_for_segment (pplug, ephemeral_heap_segment))
            {
                consing_gen = ensure_ephemeral_heap_segment (consing_gen);
                //allocate all of the generation gaps
                while (active_new_gen_number > 0)
                {
                    active_new_gen_number--;

                    if (active_new_gen_number == (max_generation - 1))
                    {
                        // 若是要防止gen 1的pinned plug降代則須要調用調用advance_pins_for_demotion跳過(出隊)它們
                        // 在原來gen 0中的pinned plug不會改變
                        maxgen_pinned_compact_before_advance = generation_pinned_allocation_compact_size (generation_of (max_generation));
                        if (!demote_gen1_p)
                            advance_pins_for_demotion (consing_gen);
                    }

                    // 計劃剩餘的代邊界
                    generation* gen = generation_of (active_new_gen_number);
                    plan_generation_start (gen, consing_gen, 0);

                    // 代邊界被設置到pinned plug以前的時候須要記錄降代的範圍(降代已經實際發生,設置demotion_low是記錄降代的範圍)
                    if (demotion_low == MAX_PTR)
                    {
                        demotion_low = pplug;
                        dprintf (3, ("end plan: dlow->%Ix", demotion_low));
                    }

                    dprintf (2, ("(%d)gen%d plan start: %Ix", 
                                  heap_number, active_new_gen_number, (size_t)generation_plan_allocation_start (gen)));
                    assert (generation_plan_allocation_start (gen));
                }
            }
        }

        // 全部pinned plug都已出隊時跳出
        if (pinned_plug_que_empty_p())
            break;

        // 出隊一個pinned plug
        size_t  entry = deque_pinned_plug();
        mark*  m = pinned_plug_of (entry);
        uint8_t*  plug = pinned_plug (m);
        size_t  len = pinned_len (m);

        // 檢測這個pinned plug是否在cosing_gen的allocation segment以外
        // 若是不在須要調整allocation segment,等會須要把generation_allocation_pointer設置爲plug + len
        // detect pinned block in different segment (later) than
        // allocation segment
        heap_segment* nseg = heap_segment_rw (generation_allocation_segment (consing_gen));

        while ((plug < generation_allocation_pointer (consing_gen)) ||
               (plug >= heap_segment_allocated (nseg)))
        {
            assert ((plug < heap_segment_mem (nseg)) ||
                    (plug > heap_segment_reserved (nseg)));
            //adjust the end of the segment to be the end of the plug
            assert (generation_allocation_pointer (consing_gen)>=
                    heap_segment_mem (nseg));
            assert (generation_allocation_pointer (consing_gen)<=
                    heap_segment_committed (nseg));

            heap_segment_plan_allocated (nseg) =
                generation_allocation_pointer (consing_gen);
            //switch allocation segment
            nseg = heap_segment_next_rw (nseg);
            generation_allocation_segment (consing_gen) = nseg;
            //reset the allocation pointer and limits
            generation_allocation_pointer (consing_gen) =
                heap_segment_mem (nseg);
        }

        // 出隊之後設置len = pinned plug - generation_allocation_pointer (consing_gen)
        // 表示pinned plug的開始地址離最後的模擬壓縮分配地址的空間,這個空間能夠變成free object
        set_new_pin_info (m, generation_allocation_pointer (consing_gen));
        dprintf (2, ("pin %Ix b: %Ix->%Ix", plug, brick_of (plug),
            (size_t)(brick_table[brick_of (plug)])));

        // 設置模擬壓縮分配地址到plug的結尾
        generation_allocation_pointer (consing_gen) = plug + len;
        generation_allocation_limit (consing_gen) =
            generation_allocation_pointer (consing_gen);
        //Add the size of the pinned plug to the right pinned allocations
        //find out which gen this pinned plug came from 
        int frgn = object_gennum (plug);

        // 統計清掃時會多出的pinned object大小
        // 加到上一代中(pinned object升代)
        if ((frgn != (int)max_generation) && settings.promotion)
        {
            generation_pinned_allocation_sweep_size ((generation_of (frgn +1))) += len;
        }
    }

    // 計劃剩餘全部代的邊界
    // 大部分狀況下(升代 + 無降代)這裏會設置gen 0的邊界,也就是在現有的全部存活對象以後
    plan_generation_starts (consing_gen);

    // 打印除錯信息
    print_free_and_plug ("AP");

    // 打印除錯信息
    {
#ifdef SIMPLE_DPRINTF
        for (int gen_idx = 0; gen_idx <= max_generation; gen_idx++)
        {
            generation* temp_gen = generation_of (gen_idx);
            dynamic_data* temp_dd = dynamic_data_of (gen_idx);

            int added_pinning_ratio = 0;
            int artificial_pinned_ratio = 0;

            if (dd_pinned_survived_size (temp_dd) != 0)
            {
                added_pinning_ratio = (int)((float)dd_added_pinned_size (temp_dd) * 100 / (float)dd_pinned_survived_size (temp_dd));
                artificial_pinned_ratio = (int)((float)dd_artificial_pinned_survived_size (temp_dd) * 100 / (float)dd_pinned_survived_size (temp_dd));
            }

            size_t padding_size = 
#ifdef SHORT_PLUGS
                dd_padding_size (temp_dd);
#else
                0;
#endif //SHORT_PLUGS
            dprintf (1, ("gen%d: %Ix, %Ix(%Id), NON PIN alloc: %Id, pin com: %Id, sweep: %Id, surv: %Id, pinsurv: %Id(%d%% added, %d%% art), np surv: %Id, pad: %Id",
                gen_idx, 
                generation_allocation_start (temp_gen),
                generation_plan_allocation_start (temp_gen),
                (size_t)(generation_plan_allocation_start (temp_gen) - generation_allocation_start (temp_gen)),
                generation_allocation_size (temp_gen),
                generation_pinned_allocation_compact_size (temp_gen),
                generation_pinned_allocation_sweep_size (temp_gen),
                dd_survived_size (temp_dd),
                dd_pinned_survived_size (temp_dd),
                added_pinning_ratio,
                artificial_pinned_ratio,
                (dd_survived_size (temp_dd) - dd_pinned_survived_size (temp_dd)),
                padding_size));
        }
#endif //SIMPLE_DPRINTF
    }

    // 繼續打印除錯信息,而且更新gen 2的統計信息
    if (settings.condemned_generation == (max_generation - 1 ))
    {
        size_t plan_gen2_size = generation_plan_size (max_generation);
        size_t growth = plan_gen2_size - old_gen2_size;

        if (growth > 0)
        {
            dprintf (1, ("gen2 grew %Id (end seg alloc: %Id, gen1 c alloc: %Id", 
                growth, generation_end_seg_allocated (generation_of (max_generation)), 
                generation_condemned_allocated (generation_of (max_generation - 1))));
        }
        else
        {
            dprintf (1, ("gen2 shrank %Id (end seg alloc: %Id, gen1 c alloc: %Id", 
                (old_gen2_size - plan_gen2_size), generation_end_seg_allocated (generation_of (max_generation)), 
                generation_condemned_allocated (generation_of (max_generation - 1))));
        }

        generation* older_gen = generation_of (settings.condemned_generation + 1);
        size_t rejected_free_space = generation_free_obj_space (older_gen) - r_free_obj_space;
        size_t free_list_allocated = generation_free_list_allocated (older_gen) - r_older_gen_free_list_allocated;
        size_t end_seg_allocated = generation_end_seg_allocated (older_gen) - r_older_gen_end_seg_allocated;
        size_t condemned_allocated = generation_condemned_allocated (older_gen) - r_older_gen_condemned_allocated;

        dprintf (1, ("older gen's free alloc: %Id->%Id, seg alloc: %Id->%Id, condemned alloc: %Id->%Id",
                    r_older_gen_free_list_allocated, generation_free_list_allocated (older_gen),
                    r_older_gen_end_seg_allocated, generation_end_seg_allocated (older_gen), 
                    r_older_gen_condemned_allocated, generation_condemned_allocated (older_gen)));

        dprintf (1, ("this GC did %Id free list alloc(%Id bytes free space rejected), %Id seg alloc and %Id condemned alloc, gen1 condemned alloc is %Id", 
            free_list_allocated, rejected_free_space, end_seg_allocated,
            condemned_allocated, generation_condemned_allocated (generation_of (settings.condemned_generation))));

        maxgen_size_increase* maxgen_size_info = &(get_gc_data_per_heap()->maxgen_size_info);
        maxgen_size_info->free_list_allocated = free_list_allocated;
        maxgen_size_info->free_list_rejected = rejected_free_space;
        maxgen_size_info->end_seg_allocated = end_seg_allocated;
        maxgen_size_info->condemned_allocated = condemned_allocated;
        maxgen_size_info->pinned_allocated = maxgen_pinned_compact_before_advance;
        maxgen_size_info->pinned_allocated_advance = generation_pinned_allocation_compact_size (generation_of (max_generation)) - maxgen_pinned_compact_before_advance;

#ifdef FREE_USAGE_STATS
        int free_list_efficiency = 0;
        if ((free_list_allocated + rejected_free_space) != 0)
            free_list_efficiency = (int)(((float) (free_list_allocated) / (float)(free_list_allocated + rejected_free_space)) * (float)100);

        int running_free_list_efficiency = (int)(generation_allocator_efficiency(older_gen)*100);

        dprintf (1, ("gen%d free list alloc effi: %d%%, current effi: %d%%",
                    older_gen->gen_num,
                    free_list_efficiency, running_free_list_efficiency));

        dprintf (1, ("gen2 free list change"));
        for (int j = 0; j < NUM_GEN_POWER2; j++)
        {
            dprintf (1, ("[h%d][#%Id]: 2^%d: F: %Id->%Id(%Id), P: %Id", 
                heap_number, 
                settings.gc_index,
                (j + 10), r_older_gen_free_space[j], older_gen->gen_free_spaces[j], 
                (ptrdiff_t)(r_older_gen_free_space[j] - older_gen->gen_free_spaces[j]),
                (generation_of(max_generation - 1))->gen_plugs[j]));
        }
#endif //FREE_USAGE_STATS
    }

    // 計算碎片空間大小fragmentation
    // 這個是判斷是否要壓縮的依據之一
    // 算法簡略以下
    // frag = (heap_segment_allocated(ephemeral_heap_segment) - generation_allocation_pointer (consing_gen))
    // for segment in non_ephemeral_segments
    //     frag += heap_segment_allocated (seg) - heap_segment_plan_allocated (seg)
    // for plug in dequed_plugs
    //     frag += plug.len
    size_t fragmentation =
        generation_fragmentation (generation_of (condemned_gen_number),
                                  consing_gen,
                                  heap_segment_allocated (ephemeral_heap_segment));

    dprintf (2,("Fragmentation: %Id", fragmentation));
    dprintf (2,("---- End of Plan phase ----"));

    // 統計計劃階段的結束時間
#ifdef TIME_GC
    finish = GetCycleCount32();
    plan_time = finish - start;
#endif //TIME_GC

    // We may update write barrier code.  We assume here EE has been suspended if we are on a GC thread.
    assert(GCHeap::IsGCInProgress());

    // 是否要擴展(使用新的segment heap segment)
    BOOL should_expand = FALSE;
    // 是否要壓縮
    BOOL should_compact= FALSE;
    ephemeral_promotion = FALSE;

    // 若是內存過小應該強制開啓壓縮
#ifdef BIT64
    if ((!settings.concurrent) &&
        ((condemned_gen_number < max_generation) && 
         ((settings.gen0_reduction_count > 0) || (settings.entry_memory_load >= 95))))
    {
        dprintf (2, ("gen0 reduction count is %d, condemning %d, mem load %d",
                     settings.gen0_reduction_count,
                     condemned_gen_number,
                     settings.entry_memory_load));
        should_compact = TRUE;

        get_gc_data_per_heap()->set_mechanism (gc_heap_compact, 
            ((settings.gen0_reduction_count > 0) ? compact_fragmented_gen0 : compact_high_mem_load));

        // 若是ephemeal heap segment空間較少應該換一個新的segment
        if ((condemned_gen_number >= (max_generation - 1)) && 
            dt_low_ephemeral_space_p (tuning_deciding_expansion))
        {
            dprintf (2, ("Not enough space for all ephemeral generations with compaction"));
            should_expand = TRUE;
        }
    }
    else
    {
#endif // BIT64
        // 判斷是否要壓縮
        // 請看下面函數decide_on_compacting的代碼解釋
        should_compact = decide_on_compacting (condemned_gen_number, fragmentation, should_expand);
#ifdef BIT64
    }
#endif // BIT64

    // 判斷是否要壓縮大對象的堆
#ifdef FEATURE_LOH_COMPACTION
    loh_compacted_p = FALSE;
#endif //FEATURE_LOH_COMPACTION

    if (condemned_gen_number == max_generation)
    {
#ifdef FEATURE_LOH_COMPACTION
        if (settings.loh_compaction)
        {
            // 針對大對象的堆模擬壓縮,和前面建立plug計算reloc的處理差很少,可是一個plug中只有一個對象,也不會有plug樹
            // 保存plug信息使用的類型是loh_obj_and_pad
            if (plan_loh())
            {
                should_compact = TRUE;
                get_gc_data_per_heap()->set_mechanism (gc_heap_compact, compact_loh_forced);
                loh_compacted_p = TRUE;
            }
        }
        else
        {
            // 清空loh_pinned_queue
            if ((heap_number == 0) && (loh_pinned_queue))
            {
                loh_pinned_queue_decay--;

                if (!loh_pinned_queue_decay)
                {
                    delete loh_pinned_queue;
                    loh_pinned_queue = 0;
                }
            }
        }

        // 若是不須要壓縮大對象的堆,在這裏執行清掃
        // 把未標記的對象合併到一個free object而且加到free list中
        // 請參考後面sweep phase的代碼解釋
        if (!loh_compacted_p)
#endif //FEATURE_LOH_COMPACTION
        {
#if defined(GC_PROFILING) || defined(FEATURE_EVENT_TRACE)
            if (ShouldTrackMovementForProfilerOrEtw())
                notify_profiler_of_surviving_large_objects();
#endif // defined(GC_PROFILING) || defined(FEATURE_EVENT_TRACE)
            sweep_large_objects();
        }
    }
    else
    {
        settings.loh_compaction = FALSE;
    }

#ifdef MULTIPLE_HEAPS
    // 若是存在多個heap(服務器GC)還須要投票從新決定should_compact和should_expand
    // 這裏的一些處理(例如刪除大對象segment和設置settings.demotion)是服務器GC和工做站GC都會作的

    new_heap_segment = NULL;

    if (should_compact && should_expand)
        gc_policy = policy_expand;
    else if (should_compact)
        gc_policy = policy_compact;
    else
        gc_policy = policy_sweep;

    //vote for result of should_compact
    dprintf (3, ("Joining for compaction decision"));
    gc_t_join.join(this, gc_join_decide_on_compaction);
    if (gc_t_join.joined())
    {
        // 刪除空的(無存活對象的)大對象segment
        //safe place to delete large heap segments
        if (condemned_gen_number == max_generation)
        {
            for (int i = 0; i < n_heaps; i++)
            {
                g_heaps [i]->rearrange_large_heap_segments ();
            }
        }

        settings.demotion = FALSE;
        int pol_max = policy_sweep;
#ifdef GC_CONFIG_DRIVEN
        BOOL is_compaction_mandatory = FALSE;
#endif //GC_CONFIG_DRIVEN

        int i;
        for (i = 0; i < n_heaps; i++)
        {
            if (pol_max < g_heaps[i]->gc_policy)
                pol_max = policy_compact;
            // set the demotion flag is any of the heap has demotion
            if (g_heaps[i]->demotion_high >= g_heaps[i]->demotion_low)
            {
                (g_heaps[i]->get_gc_data_per_heap())->set_mechanism_bit (gc_demotion_bit);
                settings.demotion = TRUE;
            }

#ifdef GC_CONFIG_DRIVEN
            if (!is_compaction_mandatory)
            {
                int compact_reason = (g_heaps[i]->get_gc_data_per_heap())->get_mechanism (gc_heap_compact);
                if (compact_reason >= 0)
                {
                    if (gc_heap_compact_reason_mandatory_p[compact_reason])
                        is_compaction_mandatory = TRUE;
                }
            }
#endif //GC_CONFIG_DRIVEN
        }

#ifdef GC_CONFIG_DRIVEN
        if (!is_compaction_mandatory)
        {
            // If compaction is not mandatory we can feel free to change it to a sweeping GC.
            // Note that we may want to change this to only checking every so often instead of every single GC.
            if (should_do_sweeping_gc (pol_max >= policy_compact))
            {
                pol_max = policy_sweep;
            }
            else
            {
                if (pol_max == policy_sweep)
                    pol_max = policy_compact;
            }
        }
#endif //GC_CONFIG_DRIVEN

        for (i = 0; i < n_heaps; i++)
        {
            if (pol_max > g_heaps[i]->gc_policy)
                g_heaps[i]->gc_policy = pol_max;
            //get the segment while we are serialized
            if (g_heaps[i]->gc_policy == policy_expand)
            {
                g_heaps[i]->new_heap_segment =
                     g_heaps[i]->soh_get_segment_to_expand();
                if (!g_heaps[i]->new_heap_segment)
                {
                    set_expand_in_full_gc (condemned_gen_number);
                    //we are out of memory, cancel the expansion
                    g_heaps[i]->gc_policy = policy_compact;
                }
            }
        }

        BOOL is_full_compacting_gc = FALSE;

        if ((gc_policy >= policy_compact) && (condemned_gen_number == max_generation))
        {
            full_gc_counts[gc_type_compacting]++;
            is_full_compacting_gc = TRUE;
        }

        for (i = 0; i < n_heaps; i++)
        {
            //copy the card and brick tables
            if (g_card_table!= g_heaps[i]->card_table)
            {
                g_heaps[i]->copy_brick_card_table();
            }

            if (is_full_compacting_gc)
            {
                g_heaps[i]->loh_alloc_since_cg = 0;
            }
        }

        //start all threads on the roots.
        dprintf(3, ("Starting all gc threads after compaction decision"));
        gc_t_join.restart();
    }

    //reset the local variable accordingly
    should_compact = (gc_policy >= policy_compact);
    should_expand  = (gc_policy >= policy_expand);

#else //MULTIPLE_HEAPS

    // 刪除空的(無存活對象的)大對象segment
    //safe place to delete large heap segments
    if (condemned_gen_number == max_generation)
    {
        rearrange_large_heap_segments ();
    }

    // 若是有對象被降代,則設置settings.demotion = true
    settings.demotion = ((demotion_high >= demotion_low) ? TRUE : FALSE);
    if (settings.demotion)
        get_gc_data_per_heap()->set_mechanism_bit (gc_demotion_bit);

    // 若是壓縮不是必須的,根據用戶提供的特殊設置從新設置should_compact
#ifdef GC_CONFIG_DRIVEN
    BOOL is_compaction_mandatory = FALSE;
    int compact_reason = get_gc_data_per_heap()->get_mechanism (gc_heap_compact);
    if (compact_reason >= 0)
        is_compaction_mandatory = gc_heap_compact_reason_mandatory_p[compact_reason];

    if (!is_compaction_mandatory)
    {
        if (should_do_sweeping_gc (should_compact))
            should_compact = FALSE;
        else
            should_compact = TRUE;
    }
#endif //GC_CONFIG_DRIVEN

    if (should_compact && (condemned_gen_number == max_generation))
    {
        full_gc_counts[gc_type_compacting]++;
        loh_alloc_since_cg = 0;
    }
#endif //MULTIPLE_HEAPS

    // 進入重定位和壓縮階段
    if (should_compact)
    {
        dprintf (2,( "**** Doing Compacting GC ****"));

        // 若是應該使用新的ephemeral heap segment,調用expand_heap
        // expand_heap有可能會複用前面的segment,也有可能從新生成一個segment
        if (should_expand)
        {
#ifndef MULTIPLE_HEAPS
            heap_segment* new_heap_segment = soh_get_segment_to_expand();
#endif //!MULTIPLE_HEAPS
            if (new_heap_segment)
            {
                consing_gen = expand_heap(condemned_gen_number,
                                          consing_gen,
                                          new_heap_segment);
            }

            // If we couldn't get a new segment, or we were able to 
            // reserve one but no space to commit, we couldn't
            // expand heap.
            if (ephemeral_heap_segment != new_heap_segment)
            {
                set_expand_in_full_gc (condemned_gen_number);
                should_expand = FALSE;
            }
        }
        generation_allocation_limit (condemned_gen1) =
            generation_allocation_pointer (condemned_gen1);
        if ((condemned_gen_number < max_generation))
        {
            generation_allocator (older_gen)->commit_alloc_list_changes();

            // 若是 generation_allocation_limit 等於 heap_segment_plan_allocated
            //      設置 heap_segment_plan_allocated 等於 generation_allocation_pointer
            //      設置 generation_allocation_limit 等於 generation_allocation_pointer
            // 不然
            //      在alloc_ptr到limit的空間建立一個free object, 不加入free list
            // Fix the allocation area of the older generation
            fix_older_allocation_area (older_gen);
        }
        assert (generation_allocation_segment (consing_gen) ==
                ephemeral_heap_segment);

#if defined(GC_PROFILING) || defined(FEATURE_EVENT_TRACE)
        if (ShouldTrackMovementForProfilerOrEtw())
        {
            record_survived_for_profiler(condemned_gen_number, first_condemned_address);
        }
#endif // defined(GC_PROFILING) || defined(FEATURE_EVENT_TRACE)

        // 調用重定位階段
        // 這裏會修改全部須要移動的對象的指針地址,可是不會移動它們的內容
        // 具體代碼請看後面
        relocate_phase (condemned_gen_number, first_condemned_address);

        // 調用壓縮階段
        // 這裏會複製對象的內容到它們移動到的地址
        // 具體代碼請看後面
        compact_phase (condemned_gen_number, first_condemned_address,
                       (!settings.demotion && settings.promotion));
        
        // fix_generation_bounds作的事情以下
        // - 應用各個代的計劃代邊界
        //   - generation_allocation_start (gen) = generation_plan_allocation_start (gen)
        //   - generation_allocation_pointer (gen) = 0;
        //   - generation_allocation_limit (gen) = 0;
        // - 代邊界的開始會留一段min_obj_size的空間,把這段空間變爲free object
        // - 若是ephemeral segment已改變則設置舊ephemeral segment的start到allocated的整個範圍到Card Table
        // - 設置ephemeral_heap_segment的allocated到plan_allocated
        fix_generation_bounds (condemned_gen_number, consing_gen);
        assert (generation_allocation_limit (youngest_generation) ==
                generation_allocation_pointer (youngest_generation));
        
        // 刪除空的(無存活對象的)小對象segment
        // 修復segment鏈表,若是ephemeral heap segment由於expand_heap改變了這裏會從新正確的連接各個segment
        // 修復segment的處理
        // - 若是segment的next是null且堆段不是ephemeral segment, 則next = ephemeral segment
        // - 若是segment是ephemeral_heap_segment而且有next, 則單獨把這個segment抽出來(prev.next = next)
        // - 調用delete_heap_segment刪除無存活對象的segment
        // - 設置heap_segment_allocated (seg) = heap_segment_plan_allocated (seg)
        // - 若是segment不是ephemeral segment, 則調用decommit_heap_segment_pages釋放allocated到committed的內存
        if (condemned_gen_number >= (max_generation -1))
        {
#ifdef MULTIPLE_HEAPS
            // this needs be serialized just because we have one
            // segment_standby_list/seg_table for all heaps. We should make it at least
            // so that when hoarding is not on we don't need this join because
            // decommitting memory can take a long time.
            //must serialize on deleting segments
            gc_t_join.join(this, gc_join_rearrange_segs_compaction);
            if (gc_t_join.joined())
            {
                for (int i = 0; i < n_heaps; i++)
                {
                    g_heaps[i]->rearrange_heap_segments(TRUE);
                }
                gc_t_join.restart();
            }
#else
            rearrange_heap_segments(TRUE);
#endif //MULTIPLE_HEAPS

            // 從新設置第0代和第1代的generation_start_segment和generation_allocation_segment到新的ephemeral_heap_segment
            if (should_expand)
            {
                //fix the start_segment for the ephemeral generations
                for (int i = 0; i < max_generation; i++)
                {
                    generation* gen = generation_of (i);
                    generation_start_segment (gen) = ephemeral_heap_segment;
                    generation_allocation_segment (gen) = ephemeral_heap_segment;
                }
            }
        }

        {
            // 由於析構隊列中的對象分代儲存,這裏根據升代或者降代移動析構隊列中的對象
#ifdef FEATURE_PREMORTEM_FINALIZATION
            finalize_queue->UpdatePromotedGenerations (condemned_gen_number,
                                                       (!settings.demotion && settings.promotion));
#endif // FEATURE_PREMORTEM_FINALIZATION

#ifdef MULTIPLE_HEAPS
            dprintf(3, ("Joining after end of compaction"));
            gc_t_join.join(this, gc_join_adjust_handle_age_compact);
            if (gc_t_join.joined())
#endif //MULTIPLE_HEAPS
            {
#ifdef MULTIPLE_HEAPS
                //join all threads to make sure they are synchronized
                dprintf(3, ("Restarting after Promotion granted"));
                gc_t_join.restart();
#endif //MULTIPLE_HEAPS
            }

            // 更新GC Handle表中記錄的代數
            // GcPromotionsGranted的處理:
            //    調用 Ref_AgeHandles(condemned, max_gen, (uintptr_t)sc)
            // GcDemote的處理:
            //    調用 Ref_RejuvenateHandles (condemned, max_gen, (uintptr_t)sc)
            // Ref_AgeHandles的處理:
            //    掃描g_HandleTableMap中的HandleTable, 逐個調用 BlockAgeBlocks
            //    BlockAgeBlocks會增長rgGeneration+uBlock~uCount中的數字
            //    0x00ffffff => 0x01ffffff => 0x02ffffff
            //    #define COMPUTE_AGED_CLUMPS(gen, msk)       APPLY_CLUMP_ADDENDS(gen, COMPUTE_CLUMP_ADDENDS(gen, msk))
            //    #define COMPUTE_AGED_CLUMPS(gen, msk)       gen + COMPUTE_CLUMP_ADDENDS(gen, msk)
            //    #define COMPUTE_AGED_CLUMPS(gen, msk)       gen + MAKE_CLUMP_MASK_ADDENDS(COMPUTE_CLUMP_MASK(gen, msk))
            //    #define COMPUTE_AGED_CLUMPS(gen, msk)       gen + MAKE_CLUMP_MASK_ADDENDS((((gen & GEN_CLAMP) - msk) & GEN_MASK))
            //    #define COMPUTE_AGED_CLUMPS(gen, msk)       gen + (((((gen & GEN_CLAMP) - msk) & GEN_MASK)) >> GEN_INC_SHIFT)
            //    #define COMPUTE_AGED_CLUMPS(gen, msk)       gen + (((((gen & 0x3F3F3F3F) - msk) & 0x40404040)) >> 6)
            //    #define GEN_FULLGC                          PREFOLD_FILL_INTO_AGEMASK(GEN_AGE_LIMIT)
            //    #define GEN_FULLGC                          PREFOLD_FILL_INTO_AGEMASK(0x3E3E3E3E)
            //    #define GEN_FULLGC                          (1 + (0x3E3E3E3E) + (~GEN_FILL))
            //    #define GEN_FULLGC                          (1 + (0x3E3E3E3E) + (~0x80808080))
            //    #define GEN_FULLGC                          0xbfbfbfbe
            // Ref_RejuvenateHandles的處理:    
            //    掃描g_HandleTableMap中的HandleTable, 逐個調用 BlockResetAgeMapForBlocks
            //    BlockAgeBlocks會減小rgGeneration+uBlock~uCount中的數字
            //    取決於該block中的handle中最年輕的代數
            // rgGeneration
            //    一個block對應4 byte, 第一個byte表明該block中的GCHandle的代
            ScanContext sc;
            sc.thread_number = heap_number;
            sc.promotion = FALSE;
            sc.concurrent = FALSE;
            // new generations bounds are set can call this guy
            if (settings.promotion && !settings.demotion)
            {
                dprintf (2, ("Promoting EE roots for gen %d",
                             condemned_gen_number));
                GCScan::GcPromotionsGranted(condemned_gen_number,
                                                max_generation, &sc);
            }
            else if (settings.demotion)
            {
                dprintf (2, ("Demoting EE roots for gen %d",
                             condemned_gen_number));
                GCScan::GcDemote (condemned_gen_number, max_generation, &sc);
            }
        }

        // 把各個pinned plug前面的空餘空間(出隊後的len)變爲free object並加到free list中
        {
            gen0_big_free_spaces = 0;

            // 隊列底部等於0
            reset_pinned_queue_bos();
            unsigned int  gen_number = min (max_generation, 1 + condemned_gen_number);
            generation*  gen = generation_of (gen_number);
            uint8_t*  low = generation_allocation_start (generation_of (gen_number-1));
            uint8_t*  high =  heap_segment_allocated (ephemeral_heap_segment);
            
            while (!pinned_plug_que_empty_p())
            {
                // 出隊
                mark*  m = pinned_plug_of (deque_pinned_plug());
                size_t len = pinned_len (m);
                uint8_t*  arr = (pinned_plug (m) - len);
                dprintf(3,("free [%Ix %Ix[ pin",
                            (size_t)arr, (size_t)arr + len));
                if (len != 0)
                {
                    // 在pinned plug前的空餘空間建立free object
                    assert (len >= Align (min_obj_size));
                    make_unused_array (arr, len);
                    // fix fully contained bricks + first one
                    // if the array goes beyong the first brick
                    size_t start_brick = brick_of (arr);
                    size_t end_brick = brick_of (arr + len);
                    // 若是free object橫跨多個brick,更新brick表
                    if (end_brick != start_brick)
                    {
                        dprintf (3,
                                    ("Fixing bricks [%Ix, %Ix[ to point to unused array %Ix",
                                    start_brick, end_brick, (size_t)arr));
                        set_brick (start_brick,
                                    arr - brick_address (start_brick));
                        size_t brick = start_brick+1;
                        while (brick < end_brick)
                        {
                            set_brick (brick, start_brick - brick);
                            brick++;
                        }
                    }

                    // 判斷要加到哪一個代的free list中
                    //when we take an old segment to make the new
                    //ephemeral segment. we can have a bunch of
                    //pinned plugs out of order going to the new ephemeral seg
                    //and then the next plugs go back to max_generation
                    if ((heap_segment_mem (ephemeral_heap_segment) <= arr) &&
                        (heap_segment_reserved (ephemeral_heap_segment) > arr))
                    {

                        while ((low <= arr) && (high > arr))
                        {
                            gen_number--;
                            assert ((gen_number >= 1) || (demotion_low != MAX_PTR) ||
                                    settings.demotion || !settings.promotion);
                            dprintf (3, ("new free list generation %d", gen_number));

                            gen = generation_of (gen_number);
                            if (gen_number >= 1)
                                low = generation_allocation_start (generation_of (gen_number-1));
                            else
                                low = high;
                        }
                    }
                    else
                    {
                        dprintf (3, ("new free list generation %d", max_generation));
                        gen_number = max_generation;
                        gen = generation_of (gen_number);
                    }

                    // 加到free list中
                    dprintf(3,("threading it into generation %d", gen_number));
                    thread_gap (arr, len, gen);
                    add_gen_free (gen_number, len);
                }
            }
        }

#ifdef _DEBUG
        for (int x = 0; x <= max_generation; x++)
        {
            assert (generation_allocation_start (generation_of (x)));
        }
#endif //_DEBUG

        // 若是已經升代了,原來gen 0的對象會變爲gen 1
        // 清理當前gen 1在Card Table中的標記
        if (!settings.demotion && settings.promotion)
        {
            //clear card for generation 1. generation 0 is empty
            clear_card_for_addresses (
                generation_allocation_start (generation_of (1)),
                generation_allocation_start (generation_of (0)));
        }
        // 若是已經升代了,確認代0的只包含一個對象(一個最小大小的free object)
        if (settings.promotion && !settings.demotion)
        {
            uint8_t* start = generation_allocation_start (youngest_generation);
            MAYBE_UNUSED_VAR(start);
            assert (heap_segment_allocated (ephemeral_heap_segment) ==
                    (start + Align (size (start))));
        }
    }
    // 進入清掃階段
    // 清掃階段的關鍵處理在make_free_lists中,目前你看不到叫`sweep_phase`的函數,這裏就是sweep phase
    else
    {
        // 清掃階段必須升代
        //force promotion for sweep
        settings.promotion = TRUE;
        settings.compaction = FALSE;

        ScanContext sc;
        sc.thread_number = heap_number;
        sc.promotion = FALSE;
        sc.concurrent = FALSE;

        dprintf (2, ("**** Doing Mark and Sweep GC****"));

        // 恢復對舊代成員的備份
        if ((condemned_gen_number < max_generation))
        {
            generation_allocator (older_gen)->copy_from_alloc_list (r_free_list);
            generation_free_list_space (older_gen) = r_free_list_space;
            generation_free_obj_space (older_gen) = r_free_obj_space;
            generation_free_list_allocated (older_gen) = r_older_gen_free_list_allocated;
            generation_end_seg_allocated (older_gen) = r_older_gen_end_seg_allocated;
            generation_condemned_allocated (older_gen) = r_older_gen_condemned_allocated;
            generation_allocation_limit (older_gen) = r_allocation_limit;
            generation_allocation_pointer (older_gen) = r_allocation_pointer;
            generation_allocation_context_start_region (older_gen) = r_allocation_start_region;
            generation_allocation_segment (older_gen) = r_allocation_segment;
        }

        // 若是 generation_allocation_limit 等於 heap_segment_plan_allocated
        //      設置 heap_segment_plan_allocated 等於 generation_allocation_pointer
        //      設置 generation_allocation_limit 等於 generation_allocation_pointer
        // 不然
        //      在alloc_ptr到limit的空間建立一個free object, 不加入free list
        if ((condemned_gen_number < max_generation))
        {
            // Fix the allocation area of the older generation
            fix_older_allocation_area (older_gen);
        }

#if defined(GC_PROFILING) || defined(FEATURE_EVENT_TRACE)
        if (ShouldTrackMovementForProfilerOrEtw())
        {
            record_survived_for_profiler(condemned_gen_number, first_condemned_address);
        }
#endif // defined(GC_PROFILING) || defined(FEATURE_EVENT_TRACE)

        // 把不使用的空間變爲free object並存到free list
        gen0_big_free_spaces = 0;
        make_free_lists (condemned_gen_number);

        // 恢復在saved_pre_plug和saved_post_plug保存的原始數據
        recover_saved_pinned_info();

        // 由於析構隊列中的對象分代儲存,這裏根據升代或者降代移動析構隊列中的對象
#ifdef FEATURE_PREMORTEM_FINALIZATION
        finalize_queue->UpdatePromotedGenerations (condemned_gen_number, TRUE);
#endif // FEATURE_PREMORTEM_FINALIZATION
// MTHTS: leave single thread for HT processing on plan_phase
#ifdef MULTIPLE_HEAPS
        dprintf(3, ("Joining after end of sweep"));
        gc_t_join.join(this, gc_join_adjust_handle_age_sweep);
        if (gc_t_join.joined())
#endif //MULTIPLE_HEAPS
        {
            // 更新GCHandle表中記錄的代數
            GCScan::GcPromotionsGranted(condemned_gen_number,
                                            max_generation, &sc);

            // 刪除空的(無存活對象的)小對象segment和修復segment鏈表
            // 上面有詳細的註釋
            if (condemned_gen_number >= (max_generation -1))
            {
#ifdef MULTIPLE_HEAPS
                for (int i = 0; i < n_heaps; i++)
                {
                    g_heaps[i]->rearrange_heap_segments(FALSE);
                }
#else
                rearrange_heap_segments(FALSE);
#endif //MULTIPLE_HEAPS
            }

#ifdef MULTIPLE_HEAPS
            //join all threads to make sure they are synchronized
            dprintf(3, ("Restarting after Promotion granted"));
            gc_t_join.restart();
#endif //MULTIPLE_HEAPS
        }

#ifdef _DEBUG
        for (int x = 0; x <= max_generation; x++)
        {
            assert (generation_allocation_start (generation_of (x)));
        }
#endif //_DEBUG

        // 由於已經升代了,原來gen 0的對象會變爲gen 1
        // 清理當前gen 1在Card Table中的標記
        //clear card for generation 1. generation 0 is empty
        clear_card_for_addresses (
            generation_allocation_start (generation_of (1)),
            generation_allocation_start (generation_of (0)));
        assert ((heap_segment_allocated (ephemeral_heap_segment) ==
                 (generation_allocation_start (youngest_generation) +
                  Align (min_obj_size))));
    }

    //verify_partial();
}

process_ephemeral_boundaries函數的代碼:
若是當前模擬的segment是ephemeral heap segment,這個函數會在模擬當前plug的壓縮前調用決定計劃代邊界

void gc_heap::process_ephemeral_boundaries (uint8_t* x,
                                            int& active_new_gen_number,
                                            int& active_old_gen_number,
                                            generation*& consing_gen,
                                            BOOL& allocate_in_condemned)
{
retry:
    // 判斷是否要設置計劃代邊界
    // 例如當前啓用升代
    // - active_old_gen_number是1,active_new_gen_number是2
    // - 判斷plug屬於gen 0的時候會計劃gen 1(active_new_gen_number--)的邊界
    // 例如當前不啓用升代
    // - active_old_gen_number是1,active_new_gen_number是1
    // - 判斷plug屬於gen 0的時候會計劃gen 0(active_new_gen_number--)的邊界
    if ((active_old_gen_number > 0) &&
        (x >= generation_allocation_start (generation_of (active_old_gen_number - 1))))
    {
        dprintf (1, ("crossing gen%d, x is %Ix", active_old_gen_number - 1, x));

        if (!pinned_plug_que_empty_p())
        {
            dprintf (1, ("oldest pin: %Ix(%Id)",
                pinned_plug (oldest_pin()), 
                (x - pinned_plug (oldest_pin()))));
        }

        // 若是升代
        // active_old_gen_number: 2 => 1 => 0
        // active_new_gen_number: 2 => 2 => 1
        // 若是不升代
        // active_old_gen_number: 2 => 1 => 0
        // active_new_gen_number: 2 => 1 => 0
        if (active_old_gen_number <= (settings.promotion ? (max_generation - 1) : max_generation))
        {
            active_new_gen_number--;
        }

        active_old_gen_number--;
        assert ((!settings.promotion) || (active_new_gen_number>0));

        if (active_new_gen_number == (max_generation - 1))
        {
            // 打印和設置統計信息
#ifdef FREE_USAGE_STATS
            if (settings.condemned_generation == max_generation)
            {
                // We need to do this before we skip the rest of the pinned plugs.
                generation* gen_2 = generation_of (max_generation);
                generation* gen_1 = generation_of (max_generation - 1);

                size_t total_num_pinned_free_spaces_left = 0;

                // We are about to allocate gen1, check to see how efficient fitting in gen2 pinned free spaces is.
                for (int j = 0; j < NUM_GEN_POWER2; j++)
                {
                    dprintf (1, ("[h%d][#%Id]2^%d: current: %Id, S: 2: %Id, 1: %Id(%Id)", 
                        heap_number, 
                        settings.gc_index,
                        (j + 10), 
                        gen_2->gen_current_pinned_free_spaces[j],
                        gen_2->gen_plugs[j], gen_1->gen_plugs[j],
                        (gen_2->gen_plugs[j] + gen_1->gen_plugs[j])));

                    total_num_pinned_free_spaces_left += gen_2->gen_current_pinned_free_spaces[j];
                }

                float pinned_free_list_efficiency = 0;
                size_t total_pinned_free_space = generation_allocated_in_pinned_free (gen_2) + generation_pinned_free_obj_space (gen_2);
                if (total_pinned_free_space != 0)
                {
                    pinned_free_list_efficiency = (float)(generation_allocated_in_pinned_free (gen_2)) / (float)total_pinned_free_space;
                }

                dprintf (1, ("[h%d] gen2 allocated %Id bytes with %Id bytes pinned free spaces (effi: %d%%), %Id (%Id) left",
                            heap_number,
                            generation_allocated_in_pinned_free (gen_2),
                            total_pinned_free_space, 
                            (int)(pinned_free_list_efficiency * 100),
                            generation_pinned_free_obj_space (gen_2),
                            total_num_pinned_free_spaces_left));
            }
#endif //FREE_USAGE_STATS

            // 出隊mark_stack_array中不屬於ephemeral heap segment的pinned plug,不能讓它們降代
            //Go past all of the pinned plugs for this generation.
            while (!pinned_plug_que_empty_p() &&
                   (!in_range_for_segment ((pinned_plug (oldest_pin())), ephemeral_heap_segment)))
            {
                size_t  entry = deque_pinned_plug();
                mark*  m = pinned_plug_of (entry);
                uint8_t*  plug = pinned_plug (m);
                size_t  len = pinned_len (m);
                // detect pinned block in different segment (later) than
                // allocation segment, skip those until the oldest pin is in the ephemeral seg.
                // adjust the allocation segment along the way (at the end it will
                // be the ephemeral segment.
                heap_segment* nseg = heap_segment_in_range (generation_allocation_segment (consing_gen));

                PREFIX_ASSUME(nseg != NULL);

                while (!((plug >= generation_allocation_pointer (consing_gen))&&
                        (plug < heap_segment_allocated (nseg))))
                {
                    //adjust the end of the segment to be the end of the plug
                    assert (generation_allocation_pointer (consing_gen)>=
                            heap_segment_mem (nseg));
                    assert (generation_allocation_pointer (consing_gen)<=
                            heap_segment_committed (nseg));

                    heap_segment_plan_allocated (nseg) =
                        generation_allocation_pointer (consing_gen);
                    //switch allocation segment
                    nseg = heap_segment_next_rw (nseg);
                    generation_allocation_segment (consing_gen) = nseg;
                    //reset the allocation pointer and limits
                    generation_allocation_pointer (consing_gen) =
                        heap_segment_mem (nseg);
                }
                set_new_pin_info (m, generation_allocation_pointer (consing_gen));
                assert(pinned_len(m) == 0 || pinned_len(m) >= Align(min_obj_size));
                generation_allocation_pointer (consing_gen) = plug + len;
                generation_allocation_limit (consing_gen) =
                    generation_allocation_pointer (consing_gen);
            }
            allocate_in_condemned = TRUE;
            consing_gen = ensure_ephemeral_heap_segment (consing_gen);
        }

        // active_new_gen_number不等於gen2的時候計劃它的邊界
        // gen2的邊界不會在這裏計劃,而是在前面(allocate_first_generation_start)
        if (active_new_gen_number != max_generation)
        {

            // 防止降代的時候把全部pinned plug出隊
            if (active_new_gen_number == (max_generation - 1))
            {
                maxgen_pinned_compact_before_advance = generation_pinned_allocation_compact_size (generation_of (max_generation));
                if (!demote_gen1_p)
                    advance_pins_for_demotion (consing_gen);
            }

            // 根據當前的generaion_allocation_pointer(alloc_ptr)計劃代邊界
            plan_generation_start (generation_of (active_new_gen_number), consing_gen, x);
                
            dprintf (1, ("process eph: allocated gen%d start at %Ix", 
                active_new_gen_number,
                generation_plan_allocation_start (generation_of (active_new_gen_number))));

            // 若是隊列中仍然有pinned plug
            if ((demotion_low == MAX_PTR) && !pinned_plug_que_empty_p())
            {
                // 而且最老(最左邊)的pinned plug的代數不是0的時候
                uint8_t* pplug = pinned_plug (oldest_pin());
                if (object_gennum (pplug) > 0)
                {
                    // 表示從這個pinned plug和後面的pinned plug都被降代了
                    // 設置降代範圍
                    demotion_low = pplug;
                    dprintf (3, ("process eph: dlow->%Ix", demotion_low));
                }
            }

            assert (generation_plan_allocation_start (generation_of (active_new_gen_number)));
        }

        goto retry;
    }
}

gc_heap::plan_generation_start函數的代碼以下:
根據當前的generaion_allocation_pointer(alloc_ptr)計劃代邊界

void gc_heap::plan_generation_start (generation* gen, generation* consing_gen, uint8_t* next_plug_to_allocate)
{
    // 特殊處理
    // 若是某些pinned plug很大(大於demotion_plug_len_th(6MB)),把它們出隊防止降代
#ifdef BIT64
    // We should never demote big plugs to gen0.
    if (gen == youngest_generation)
    {
        heap_segment* seg = ephemeral_heap_segment;
        size_t mark_stack_large_bos = mark_stack_bos;
        size_t large_plug_pos = 0;
        while (mark_stack_large_bos < mark_stack_tos)
        {
            if (mark_stack_array[mark_stack_large_bos].len > demotion_plug_len_th)
            {
                while (mark_stack_bos <= mark_stack_large_bos)
                {
                    size_t entry = deque_pinned_plug();
                    size_t len = pinned_len (pinned_plug_of (entry));
                    uint8_t* plug = pinned_plug (pinned_plug_of(entry));
                    if (len > demotion_plug_len_th)
                    {
                        dprintf (2, ("ps(%d): S %Ix (%Id)(%Ix)", gen->gen_num, plug, len, (plug+len)));
                    }
                    pinned_len (pinned_plug_of (entry)) = plug - generation_allocation_pointer (consing_gen);
                    assert(mark_stack_array[entry].len == 0 ||
                            mark_stack_array[entry].len >= Align(min_obj_size));
                    generation_allocation_pointer (consing_gen) = plug + len;
                    generation_allocation_limit (consing_gen) = heap_segment_plan_allocated (seg);
                    set_allocator_next_pin (consing_gen);
                }
            }

            mark_stack_large_bos++;
        }
    }
#endif // BIT64

    // 在當前consing_gen的generation_allocation_ptr建立一個最小的對象
    // 以這個對象的開始地址做爲計劃代邊界
    // 這裏的處理是保證代與代之間最少有一個對象(初始化代的時候也會這樣保證)
    generation_plan_allocation_start (gen) =
        allocate_in_condemned_generations (consing_gen, Align (min_obj_size), -1);
    
    // 壓縮後會根據這個大小把這裏的空間變爲一個free object
    generation_plan_allocation_start_size (gen) = Align (min_obj_size);

    // 若是接下來的空間很小(小於min_obj_size),則把接下來的空間也加到上面的初始對象裏
    size_t allocation_left = (size_t)(generation_allocation_limit (consing_gen) - generation_allocation_pointer (consing_gen));
    if (next_plug_to_allocate)
    {
        size_t dist_to_next_plug = (size_t)(next_plug_to_allocate - generation_allocation_pointer (consing_gen));
        if (allocation_left > dist_to_next_plug)
        {
            allocation_left = dist_to_next_plug;
        }
    }
    if (allocation_left < Align (min_obj_size))
    {
        generation_plan_allocation_start_size (gen) += allocation_left;
        generation_allocation_pointer (consing_gen) += allocation_left;
    }

    dprintf (1, ("plan alloc gen%d(%Ix) start at %Ix (ptr: %Ix, limit: %Ix, next: %Ix)", gen->gen_num, 
        generation_plan_allocation_start (gen),
        generation_plan_allocation_start_size (gen),
        generation_allocation_pointer (consing_gen), generation_allocation_limit (consing_gen),
        next_plug_to_allocate));
}

gc_heap::plan_generation_starts函數的代碼以下:
這個函數會在模擬壓縮全部對象後調用,用於計劃剩餘的代邊界,若是啓用了升代這裏會計劃gen 0的邊界

void gc_heap::plan_generation_starts (generation*& consing_gen)
{
    //make sure that every generation has a planned allocation start
    int  gen_number = settings.condemned_generation;
    while (gen_number >= 0)
    {
        // 由於不能把gen 1和gen 0的邊界放到其餘segment中
        // 這裏須要確保consing_gen的allocation segment是ephemeral heap segment
        if (gen_number < max_generation)
        {
            consing_gen = ensure_ephemeral_heap_segment (consing_gen);
        }
        // 若是這個代的邊界還沒有計劃,則執行計劃
        generation* gen = generation_of (gen_number);
        if (0 == generation_plan_allocation_start (gen))
        {
            plan_generation_start (gen, consing_gen, 0);
            assert (generation_plan_allocation_start (gen));
        }
        gen_number--;
    }
    // 設置ephemeral heap segment的計劃已分配大小
    // now we know the planned allocation size
    heap_segment_plan_allocated (ephemeral_heap_segment) =
        generation_allocation_pointer (consing_gen);
}

gc_heap::generation_fragmentation函數的代碼以下:

size_t gc_heap::generation_fragmentation (generation* gen,
                                          generation* consing_gen,
                                          uint8_t* end)
{
    size_t frag;
    // 判斷是否全部對象都壓縮到了ephemeral heap segment以前
    uint8_t* alloc = generation_allocation_pointer (consing_gen);
    // If the allocation pointer has reached the ephemeral segment
    // fine, otherwise the whole ephemeral segment is considered
    // fragmentation
    if (in_range_for_segment (alloc, ephemeral_heap_segment))
        {
            // 原allocated - 模擬壓縮的結尾allocation_pointer
            if (alloc <= heap_segment_allocated(ephemeral_heap_segment))
                frag = end - alloc;
            else
            {
                // 無一個對象存活,已經把allocated設到開始地址
                // case when no survivors, allocated set to beginning
                frag = 0;
            }
            dprintf (3, ("ephemeral frag: %Id", frag));
        }
    else
        // 全部對象都壓縮到了ephemeral heap segment以前
        // 添加整個範圍到frag
        frag = (heap_segment_allocated (ephemeral_heap_segment) -
                heap_segment_mem (ephemeral_heap_segment));
    heap_segment* seg = heap_segment_rw (generation_start_segment (gen));

    PREFIX_ASSUME(seg != NULL);

    // 添加其餘segment的原allocated - 計劃allcated
    while (seg != ephemeral_heap_segment)
    {
        frag += (heap_segment_allocated (seg) -
                 heap_segment_plan_allocated (seg));
        dprintf (3, ("seg: %Ix, frag: %Id", (size_t)seg,
                     (heap_segment_allocated (seg) -
                      heap_segment_plan_allocated (seg))));

        seg = heap_segment_next_rw (seg);
        assert (seg);
    }
    // 添加全部pinned plug前面的空餘空間
    dprintf (3, ("frag: %Id discounting pinned plugs", frag));
    //add the length of the dequeued plug free space
    size_t bos = 0;
    while (bos < mark_stack_bos)
    {
        frag += (pinned_len (pinned_plug_of (bos)));
        bos++;
    }

    return frag;
}

gc_heap::decide_on_compacting函數的代碼以下:

BOOL gc_heap::decide_on_compacting (int condemned_gen_number,
                                    size_t fragmentation,
                                    BOOL& should_expand)
{
    BOOL should_compact = FALSE;
    should_expand = FALSE;
    generation*   gen = generation_of (condemned_gen_number);
    dynamic_data* dd = dynamic_data_of (condemned_gen_number);
    size_t gen_sizes     = generation_sizes(gen);
    // 碎片空間大小 / 收集代的大小(包括更年輕的代)
    float  fragmentation_burden = ( ((0 == fragmentation) || (0 == gen_sizes)) ? (0.0f) :
                                    (float (fragmentation) / gen_sizes) );

    dprintf (GTC_LOG, ("fragmentation: %Id (%d%%)", fragmentation, (int)(fragmentation_burden * 100.0)));

    // 由Stress GC決定是否壓縮
#ifdef STRESS_HEAP
    // for pure GC stress runs we need compaction, for GC stress "mix"
    // we need to ensure a better mix of compacting and sweeping collections
    if (GCStress<cfg_any>::IsEnabled() && !settings.concurrent
        && !g_pConfig->IsGCStressMix())
        should_compact = TRUE;

     // 由Stress GC決定是否壓縮
     // 若是壓縮次數不夠清掃次數的十分之一則開啓壓縮
#ifdef GC_STATS
    // in GC stress "mix" mode, for stress induced collections make sure we 
    // keep sweeps and compactions relatively balanced. do not (yet) force sweeps
    // against the GC's determination, as it may lead to premature OOMs.
    if (g_pConfig->IsGCStressMix() && settings.stress_induced)
    {
        int compactions = g_GCStatistics.cntCompactFGC+g_GCStatistics.cntCompactNGC;
        int sweeps = g_GCStatistics.cntFGC + g_GCStatistics.cntNGC - compactions;
        if (compactions < sweeps / 10)
        {
            should_compact = TRUE;
        }
    }
#endif // GC_STATS
#endif //STRESS_HEAP

    // 判斷是否強制壓縮
    if (g_pConfig->GetGCForceCompact())
        should_compact = TRUE;

    // 是否由於OOM(Out Of Memory)致使的GC,若是是則開啓壓縮
    if ((condemned_gen_number == max_generation) && last_gc_before_oom)
    {
        should_compact = TRUE;
        last_gc_before_oom = FALSE;
        get_gc_data_per_heap()->set_mechanism (gc_heap_compact, compact_last_gc);
    }

    // gc緣由中有壓縮
    if (settings.reason == reason_induced_compacting)
    {
        dprintf (2, ("induced compacting GC"));
        should_compact = TRUE;
        get_gc_data_per_heap()->set_mechanism (gc_heap_compact, compact_induced_compacting);
    }

    dprintf (2, ("Fragmentation: %d Fragmentation burden %d%%",
                fragmentation, (int) (100*fragmentation_burden)));

    // 若是ephemeral heap segment的空間較少則開啓壓縮
    if (!should_compact)
    {
        if (dt_low_ephemeral_space_p (tuning_deciding_compaction))
        {
            dprintf(GTC_LOG, ("compacting due to low ephemeral"));
            should_compact = TRUE;
            get_gc_data_per_heap()->set_mechanism (gc_heap_compact, compact_low_ephemeral);
        }
    }

    // 若是ephemeral heap segment的空間較少,而且當前不是Full GC還須要使用新的ephemeral heap segment
    if (should_compact)
    {
        if ((condemned_gen_number >= (max_generation - 1)))
        {
            if (dt_low_ephemeral_space_p (tuning_deciding_expansion))
            {
                dprintf (GTC_LOG,("Not enough space for all ephemeral generations with compaction"));
                should_expand = TRUE;
            }
        }
    }

#ifdef BIT64
    BOOL high_memory = FALSE;
#endif // BIT64

    // 根據碎片空間大小判斷
    if (!should_compact)
    {
        // We are not putting this in dt_high_frag_p because it's not exactly
        // high fragmentation - it's just enough planned fragmentation for us to 
        // want to compact. Also the "fragmentation" we are talking about here
        // is different from anywhere else.
        // 碎片空間大小 >= dd_fragmentation_limit 或者
        // 碎片空間大小 / 收集代的大小(包括更年輕的代) >= dd_fragmentation_burden_limit 時開啓壓縮
        // 做者機器上的dd_fragmentation_limit是200000, dd_fragmentation_burden_limit是0.25
        BOOL frag_exceeded = ((fragmentation >= dd_fragmentation_limit (dd)) &&
                                (fragmentation_burden >= dd_fragmentation_burden_limit (dd)));

        if (frag_exceeded)
        {
#ifdef BACKGROUND_GC
            // do not force compaction if this was a stress-induced GC
            IN_STRESS_HEAP(if (!settings.stress_induced))
            {
#endif // BACKGROUND_GC
            assert (settings.concurrent == FALSE);
            should_compact = TRUE;
            get_gc_data_per_heap()->set_mechanism (gc_heap_compact, compact_high_frag);
#ifdef BACKGROUND_GC
            }
#endif // BACKGROUND_GC
        }

        // 若是佔用內存太高則啓用壓縮
#ifdef BIT64
        // check for high memory situation
        if(!should_compact)
        {
            uint32_t num_heaps = 1;
#ifdef MULTIPLE_HEAPS
            num_heaps = gc_heap::n_heaps;
#endif // MULTIPLE_HEAPS
            
            ptrdiff_t reclaim_space = generation_size(max_generation) - generation_plan_size(max_generation);
            if((settings.entry_memory_load >= high_memory_load_th) && (settings.entry_memory_load < v_high_memory_load_th))
            {
                if(reclaim_space > (int64_t)(min_high_fragmentation_threshold (entry_available_physical_mem, num_heaps)))
                {
                    dprintf(GTC_LOG,("compacting due to fragmentation in high memory"));
                    should_compact = TRUE;
                    get_gc_data_per_heap()->set_mechanism (gc_heap_compact, compact_high_mem_frag);
                }
                high_memory = TRUE;
            }
            else if(settings.entry_memory_load >= v_high_memory_load_th)
            {
                if(reclaim_space > (ptrdiff_t)(min_reclaim_fragmentation_threshold (num_heaps)))
                {
                    dprintf(GTC_LOG,("compacting due to fragmentation in very high memory"));
                    should_compact = TRUE;
                    get_gc_data_per_heap()->set_mechanism (gc_heap_compact, compact_vhigh_mem_frag);
                }
                high_memory = TRUE;
            }
        }
#endif // BIT64
    }

    // 測試是否能夠在ephemeral_heap_segment.allocated後面提交一段內存(從系統獲取一塊物理內存)
    // 若是失敗則啓用壓縮
    allocated (ephemeral_heap_segment);
    size_t size = Align (min_obj_size)*(condemned_gen_number+1);
    // The purpose of calling ensure_gap_allocation here is to make sure
    // that we actually are able to commit the memory to allocate generation
    // starts.
    if ((should_compact == FALSE) &&
        (ensure_gap_allocation (condemned_gen_number) == FALSE))
    {
        should_compact = TRUE;
        get_gc_data_per_heap()->set_mechanism (gc_heap_compact, compact_no_gaps);
    }

    // 若是此次Full GC的效果比較差
    // 須要減小Full GC的頻率,should_lock_elevation能夠把Full GC變爲gen 1 GC
    if (settings.condemned_generation == max_generation)
    {
        //check the progress
        if (
#ifdef BIT64
            (high_memory && !should_compact) ||
#endif // BIT64
            (generation_plan_allocation_start (generation_of (max_generation - 1)) >= 
                generation_allocation_start (generation_of (max_generation - 1))))
        {
            dprintf (2, (" Elevation: gen2 size: %d, gen2 plan size: %d, no progress, elevation = locked",
                     generation_size (max_generation),
                     generation_plan_size (max_generation)));
            //no progress -> lock
            settings.should_lock_elevation = TRUE;
        }
    }

    // 若是啓用了NoGCRegion可是仍然啓用了GC表明這是沒法從SOH(Small Object Heap)或者LOH分配到內存致使的,須要啓用壓縮
    if (settings.pause_mode == pause_no_gc)
    {
        should_compact = TRUE;
        // 若是ephemeral heap segement壓縮後的剩餘空間不足還須要設置新的ephemeral heap segment
        if ((size_t)(heap_segment_reserved (ephemeral_heap_segment) - heap_segment_plan_allocated (ephemeral_heap_segment))
            < soh_allocation_no_gc)
        {
            should_expand = TRUE;
        }
    }

    dprintf (2, ("will %s", (should_compact ? "compact" : "sweep")));
    return should_compact;
}

計劃階段在模擬壓縮和判斷後會在內部包含重定位階段(relocate_phase),壓縮階段(compact_phase)和清掃階段(sweep_phase)的處理,
接下來咱們仔細分析一下這三個階段作了什麼事情:

重定位階段(relocate_phase)

重定位階段的主要工做是修改對象的指針地址,例如A.Member的Member內存移動後,A中指向Member的指針地址也須要改變。
重定位階段只會修改指針地址,複製內存會交給下面的壓縮階段(compact_phase)完成。

以下圖:

圖中對象A和對象B引用了對象C,重定位後各個對象還在原來的位置,只是成員的地址(指針)變化了。

還記得以前標記階段(mark_phase)使用的GcScanRoots等掃描函數嗎?
這些掃描函數一樣會在重定位階段使用,只是執行的不是GCHeap::Promote而是GCHeap::Relocate
重定位對象會藉助計劃階段(plan_phase)構建的brick table和plug樹來進行快速的定位,而後對指針地址移動所屬plug的reloc位置。

重定位階段(relocate_phase)的代碼

gc_heap::relocate_phase函數的代碼以下:

void gc_heap::relocate_phase (int condemned_gen_number,
                              uint8_t* first_condemned_address)
{
    // 生成掃描上下文
    ScanContext sc;
    sc.thread_number = heap_number;
    sc.promotion = FALSE;
    sc.concurrent = FALSE;

    // 統計重定位階段的開始時間
#ifdef TIME_GC
        unsigned start;
        unsigned finish;
        start = GetCycleCount32();
#endif //TIME_GC

//  %type%  category = quote (relocate);
    dprintf (2,("---- Relocate phase -----"));

#ifdef MULTIPLE_HEAPS
    //join all threads to make sure they are synchronized
    dprintf(3, ("Joining after end of plan"));
    gc_t_join.join(this, gc_join_begin_relocate_phase);
    if (gc_t_join.joined())
#endif //MULTIPLE_HEAPS

    {
#ifdef MULTIPLE_HEAPS

        //join all threads to make sure they are synchronized
        dprintf(3, ("Restarting for relocation"));
        gc_t_join.restart();
#endif //MULTIPLE_HEAPS
    }

    // 掃描根對象(各個線程中棧和寄存器中的對象)
    // 對掃描到的各個對象調用`GCHeap::Relocate`函數
    // 注意`GCHeap::Relocate`函數不會重定位子對象,這裏只是用來重定位來源於根對象的引用
    dprintf(3,("Relocating roots"));
    GCScan::GcScanRoots(GCHeap::Relocate,
                            condemned_gen_number, max_generation, &sc);

    verify_pins_with_post_plug_info("after reloc stack");

#ifdef BACKGROUND_GC
    if (recursive_gc_sync::background_running_p())
    {
        scan_background_roots (GCHeap::Relocate, heap_number, &sc);
    }
#endif //BACKGROUND_GC

    // 非Full GC時,遍歷Card Table重定位小對象
    // 同上,`gc_heap::relocate_address`函數不會重定位子對象,這裏只是用來重定位來源於舊代的引用
    if (condemned_gen_number != max_generation)
    {
        dprintf(3,("Relocating cross generation pointers"));
        mark_through_cards_for_segments (&gc_heap::relocate_address, TRUE);
        verify_pins_with_post_plug_info("after reloc cards");
    }
    // 非Full GC時,遍歷Card Table重定位大對象
    // 同上,`gc_heap::relocate_address`函數不會重定位子對象,這裏只是用來重定位來源於舊代的引用
    if (condemned_gen_number != max_generation)
    {
        dprintf(3,("Relocating cross generation pointers for large objects"));
        mark_through_cards_for_large_objects (&gc_heap::relocate_address, TRUE);
    }
    else
    {
        // Full GC時,若是啓用了大對象壓縮則壓縮大對象的堆
#ifdef FEATURE_LOH_COMPACTION
        if (loh_compacted_p)
        {
            assert (settings.condemned_generation == max_generation);
            relocate_in_loh_compact();
        }
        else
#endif //FEATURE_LOH_COMPACTION
        {
            relocate_in_large_objects ();
        }
    }
    // 重定位存活下來的對象中的引用(收集代中的對象)
    // 枚舉brick table對各個plug中的對象調用`relocate_obj_helper`重定位它們的成員
    {
        dprintf(3,("Relocating survivors"));
        relocate_survivors (condemned_gen_number,
                            first_condemned_address);
    }

    // 掃描在析構隊列中的對象
#ifdef FEATURE_PREMORTEM_FINALIZATION
        dprintf(3,("Relocating finalization data"));
        finalize_queue->RelocateFinalizationData (condemned_gen_number,
                                                       __this);
#endif // FEATURE_PREMORTEM_FINALIZATION

    // 掃描在GC Handle表中的對象
// MTHTS
    {
        dprintf(3,("Relocating handle table"));
        GCScan::GcScanHandles(GCHeap::Relocate,
                                  condemned_gen_number, max_generation, &sc);
    }

#ifdef MULTIPLE_HEAPS
    //join all threads to make sure they are synchronized
    dprintf(3, ("Joining after end of relocation"));
    gc_t_join.join(this, gc_join_relocate_phase_done);

#endif //MULTIPLE_HEAPS

    // 統計重定位階段的結束時間
#ifdef TIME_GC
        finish = GetCycleCount32();
        reloc_time = finish - start;
#endif //TIME_GC

    dprintf(2,( "---- End of Relocate phase ----"));
}

GCHeap::Relocate函數的代碼以下:

// ppObject是保存對象地址的地址,例如&A.Member
void GCHeap::Relocate (Object** ppObject, ScanContext* sc,
                       uint32_t flags)
{
    UNREFERENCED_PARAMETER(sc);

    // 對象的地址
    uint8_t* object = (uint8_t*)(Object*)(*ppObject);
    
    THREAD_NUMBER_FROM_CONTEXT;

    //dprintf (3, ("Relocate location %Ix\n", (size_t)ppObject));
    dprintf (3, ("R: %Ix", (size_t)ppObject));

    // 空指針不處理
    if (object == 0)
        return;

    // 獲取對象所屬的gc_heap
    gc_heap* hp = gc_heap::heap_of (object);

    // 驗證對象是否合法,除錯用
    // 若是object不必定是對象的開始地址,則不作驗證
#ifdef _DEBUG
    if (!(flags & GC_CALL_INTERIOR))
    {
        // We cannot validate this object if it's in the condemned gen because it could 
        // be one of the objects that were overwritten by an artificial gap due to a pinned plug.
        if (!((object >= hp->gc_low) && (object < hp->gc_high)))
        {
            ((CObjectHeader*)object)->Validate(FALSE);
        }
    }
#endif //_DEBUG

    dprintf (3, ("Relocate %Ix\n", (size_t)object));

    uint8_t* pheader;

    // 若是object不必定是對象的開始地址,找到對象的開始地址並重定位該開始地址,而後修改ppObject
    // 例如object是0x10000008,對象的開始地址是0x10000000,重定位後是0x0fff0000則*ppObject會設爲0x0fff0008
    if ((flags & GC_CALL_INTERIOR) && gc_heap::settings.loh_compaction)
    {
        if (!((object >= hp->gc_low) && (object < hp->gc_high)))
        {
            return;
        }

        if (gc_heap::loh_object_p (object))
        {
            pheader = hp->find_object (object, 0);
            if (pheader == 0)
            {
                return;
            }

            ptrdiff_t ref_offset = object - pheader;
            hp->relocate_address(&pheader THREAD_NUMBER_ARG);
            *ppObject = (Object*)(pheader + ref_offset);
            return;
        }
    }

    // 若是object是對象的開始地址則重定位object
    {
        pheader = object;
        hp->relocate_address(&pheader THREAD_NUMBER_ARG);
        *ppObject = (Object*)pheader;
    }

    STRESS_LOG_ROOT_RELOCATE(ppObject, object, pheader, ((!(flags & GC_CALL_INTERIOR)) ? ((Object*)object)->GetGCSafeMethodTable() : 0));
}

gc_heap::relocate_address函數的代碼以下:

void gc_heap::relocate_address (uint8_t** pold_address THREAD_NUMBER_DCL)
{
    // 不在本次gc回收範圍內的對象指針不須要移動
    uint8_t* old_address = *pold_address;
    if (!((old_address >= gc_low) && (old_address < gc_high)))
#ifdef MULTIPLE_HEAPS
    {
        UNREFERENCED_PARAMETER(thread);
        if (old_address == 0)
            return;
        gc_heap* hp = heap_of (old_address);
        if ((hp == this) ||
            !((old_address >= hp->gc_low) && (old_address < hp->gc_high)))
            return;
    }
#else //MULTIPLE_HEAPS
        return ;
#endif //MULTIPLE_HEAPS
    // 根據對象找到對應的brick
    // delta translates old_address into address_gc (old_address);
    size_t  brick = brick_of (old_address);
    int    brick_entry =  brick_table [ brick ];
    uint8_t*  new_address = old_address;
    if (! ((brick_entry == 0)))
    {
    retry:
        {
            // 若是是負數則向前繼續找
            while (brick_entry < 0)
            {
                brick = (brick + brick_entry);
                brick_entry =  brick_table [ brick ];
            }
            uint8_t* old_loc = old_address;

            // 根據plug樹搜索對象所在的plug
            uint8_t* node = tree_search ((brick_address (brick) + brick_entry-1),
                                      old_loc);
            // 找到時肯定新的地址,找不到時繼續找前面的brick(有可能在上一個brick中)
            if ((node <= old_loc))
                new_address = (old_address + node_relocation_distance (node));
            else
            {
                if (node_left_p (node))
                {
                    dprintf(3,(" L: %Ix", (size_t)node));
                    new_address = (old_address +
                                   (node_relocation_distance (node) +
                                    node_gap_size (node)));
                }
                else
                {
                    brick = brick - 1;
                    brick_entry =  brick_table [ brick ];
                    goto retry;
                }
            }
        }
        // 修改對象指針的地址
        *pold_address = new_address;
        return;
    }

    // 若是對象是大對象,對象自己就是一個plug因此能夠直接取到reloc
#ifdef FEATURE_LOH_COMPACTION
    if (loh_compacted_p
#ifdef FEATURE_BASICFREEZE
        && !frozen_object_p((Object*)old_address)
#endif // FEATURE_BASICFREEZE
        )
    {
        *pold_address = old_address + loh_node_relocation_distance (old_address);
    }
    else
#endif //FEATURE_LOH_COMPACTION
    {
        *pold_address = new_address;
    }
}

gc_heap::relocate_survivors函數的代碼以下:
這個函數用於重定位存活下來的對象中的引用

void gc_heap::relocate_survivors (int condemned_gen_number,
                                  uint8_t* first_condemned_address)
{
    generation* condemned_gen = generation_of (condemned_gen_number);
    uint8_t*  start_address = first_condemned_address;
    size_t  current_brick = brick_of (start_address);
    heap_segment*  current_heap_segment = heap_segment_rw (generation_start_segment (condemned_gen));

    PREFIX_ASSUME(current_heap_segment != NULL);

    uint8_t*  end_address = 0;

    // 重設mark_stack_array隊列
    reset_pinned_queue_bos();

    // 更新gc_heap中的oldest_pinned_plug對象
    update_oldest_pinned_plug();
    
    end_address = heap_segment_allocated (current_heap_segment);

    size_t  end_brick = brick_of (end_address - 1);

    // 初始化重定位參數
    relocate_args args;
    // 本次gc的回收範圍
    args.low = gc_low;
    args.high = gc_high;
    // 當前的plug結尾是否被下一個plug覆蓋了
    args.is_shortened = FALSE
    // last_plug或者last_plug後面的pinned plug
    // 處理plug尾部數據覆蓋時須要用到它
    args.pinned_plug_entry = 0;
    // 上一個plug,用於遍歷樹時能夠從小地址到大地址遍歷(中序遍歷)
    args.last_plug = 0;
    
    while (1)
    {
        // 當前segment已經處理完
        if (current_brick > end_brick)
        {
            // 處理最後一個plug,結尾地址是heap_segment_allocated
            if (args.last_plug)
            {
                {
                    assert (!(args.is_shortened));
                    relocate_survivors_in_plug (args.last_plug,
                                                heap_segment_allocated (current_heap_segment),
                                                args.is_shortened, 
                                                args.pinned_plug_entry);
                }

                args.last_plug = 0;
            }

            // 若是有下一個segment則處理下一個
            if (heap_segment_next_rw (current_heap_segment))
            {
                current_heap_segment = heap_segment_next_rw (current_heap_segment);
                current_brick = brick_of (heap_segment_mem (current_heap_segment));
                end_brick = brick_of (heap_segment_allocated (current_heap_segment)-1);
                continue;
            }
            else
            {
                break;
            }
        }
        {
            // 若是當前brick有對應的plug樹,處理當前brick
            int brick_entry =  brick_table [ current_brick ];

            if (brick_entry >= 0)
            {
                relocate_survivors_in_brick (brick_address (current_brick) +
                                             brick_entry -1,
                                             &args);
            }
        }
        current_brick++;
    }
}

gc_heap::relocate_survivors_in_brick函數的代碼以下:

void gc_heap::relocate_survivors_in_brick (uint8_t* tree, relocate_args* args)
{
    // 遍歷plug樹
    // 會從小到大調用relocate_survivors_in_plug (中序遍歷, 藉助args->last_plug)
    // 例若有這樣的plug樹
    //     a
    //   b   c
    // d  e
    // 枚舉順序是a b d e c
    // 調用relocate_survivors_in_plug的順序是d b e a c
    assert ((tree != NULL));

    dprintf (3, ("tree: %Ix, args->last_plug: %Ix, left: %Ix, right: %Ix, gap(t): %Ix",
        tree, args->last_plug, 
        (tree + node_left_child (tree)),
        (tree + node_right_child (tree)),
        node_gap_size (tree)));

    // 處理左節點
    if (node_left_child (tree))
    {
        relocate_survivors_in_brick (tree + node_left_child (tree), args);
    }

    // 處理last_plug
    {
        uint8_t*  plug = tree;
        BOOL   has_post_plug_info_p = FALSE;
        BOOL   has_pre_plug_info_p = FALSE;

        // 若是這個plug是pinned plug
        // 獲取是否有has_pre_plug_info_p (是否覆蓋了last_plug的尾部)
        // 獲取是否有has_post_plug_info_p (是否被下一個plug覆蓋了尾部)
        if (tree == oldest_pinned_plug)
        {
            args->pinned_plug_entry = get_oldest_pinned_entry (&has_pre_plug_info_p,
                                                               &has_post_plug_info_p);
            assert (tree == pinned_plug (args->pinned_plug_entry));

            dprintf (3, ("tree is the oldest pin: %Ix", tree));
        }

        // 處理last_plug
        if (args->last_plug)
        {
            size_t  gap_size = node_gap_size (tree);
            // last_plug的結尾 = 當前plug的開始地址 - gap
            uint8_t*  gap = (plug - gap_size);
            dprintf (3, ("tree: %Ix, gap: %Ix (%Ix)", tree, gap, gap_size));
            assert (gap_size >= Align (min_obj_size));
            uint8_t*  last_plug_end = gap;

            // last_plug的尾部是否被覆蓋了
            // args->is_shortened表明last_plug是pinned_plug,被下一個unpinned plug覆蓋了尾部
            // has_pre_plug_info_p表明last_plug是unpinned plug,被下一個pinned plug覆蓋了尾部
            BOOL check_last_object_p = (args->is_shortened || has_pre_plug_info_p);

            // 處理last_plug,結尾地址是當前plug的開始地址 - gap
            {
                relocate_survivors_in_plug (args->last_plug, last_plug_end, check_last_object_p, args->pinned_plug_entry);
            }
        }
        else
        {
            assert (!has_pre_plug_info_p);
        }

        // 設置last_plug
        args->last_plug = plug;
        // 設置是否被覆蓋了尾部
        args->is_shortened = has_post_plug_info_p;
        if (has_post_plug_info_p)
        {
            dprintf (3, ("setting %Ix as shortened", plug));
        }
        dprintf (3, ("last_plug: %Ix(shortened: %d)", plug, (args->is_shortened ? 1 : 0)));
    }

    // 處理右節點
    if (node_right_child (tree))
    {
        relocate_survivors_in_brick (tree + node_right_child (tree), args);
    }
}

gc_heap::relocate_survivors_in_plug函數的代碼以下:

void gc_heap::relocate_survivors_in_plug (uint8_t* plug, uint8_t* plug_end,
                                          BOOL check_last_object_p, 
                                          mark* pinned_plug_entry)
{
    //dprintf(3,("Relocating pointers in Plug [%Ix,%Ix[", (size_t)plug, (size_t)plug_end));
    dprintf (3,("RP: [%Ix,%Ix[", (size_t)plug, (size_t)plug_end));

    // plug的結尾被覆蓋過,須要特殊的處理
    if (check_last_object_p)
    {
        relocate_shortened_survivor_helper (plug, plug_end, pinned_plug_entry);
    }
    // 通常的處理
    else
    {
        relocate_survivor_helper (plug, plug_end);
    }
}

gc_heap::relocate_survivor_helper函數的代碼以下:

void gc_heap::relocate_survivor_helper (uint8_t* plug, uint8_t* plug_end)
{
    // 枚舉plug中的對象,分別調用relocate_obj_helper函數
    uint8_t*  x = plug;
    while (x < plug_end)
    {
        size_t s = size (x);
        uint8_t* next_obj = x + Align (s);
        Prefetch (next_obj);
        relocate_obj_helper (x, s);
        assert (s > 0);
        x = next_obj;
    }
}

gc_heap::relocate_obj_helper函數的代碼以下:

inline void
gc_heap::relocate_obj_helper (uint8_t* x, size_t s)
{
    THREAD_FROM_HEAP;
    // 判斷對象中是否包含了引用
    if (contain_pointers (x))
    {
        dprintf (3, ("$%Ix$", (size_t)x));

        // 重定位這個對象的全部成員
        // 注意這裏不會包含對象自身(nostart)
        go_through_object_nostart (method_table(x), x, s, pval,
                            {
                                uint8_t* child = *pval;
                                reloc_survivor_helper (pval);
                                if (child)
                                {
                                    dprintf (3, ("%Ix->%Ix->%Ix", (uint8_t*)pval, child, *pval));
                                }
                            });

    }
    check_class_object_demotion (x);
}

gc_heap::reloc_survivor_helper函數的代碼以下:

inline void
gc_heap::reloc_survivor_helper (uint8_t** pval)
{
    // 執行重定位,relocate_address函數上面有解釋
    THREAD_FROM_HEAP;
    relocate_address (pval THREAD_NUMBER_ARG);

    // 若是對象在降代範圍中,須要設置來源位置在Card Table中的標記
    check_demotion_helper (pval, (uint8_t*)pval);
}

gc_heap::relocate_shortened_survivor_helper函數的代碼以下:

void gc_heap::relocate_shortened_survivor_helper (uint8_t* plug, uint8_t* plug_end, mark* pinned_plug_entry)
{
    uint8_t*  x = plug;

    // 若是p_plug == plug表示當前plug是pinned plug,結尾被下一個plug覆蓋
    // 若是p_plug != plug表示當前plug是unpinned plug,結尾被p_plug覆蓋
    uint8_t* p_plug = pinned_plug (pinned_plug_entry);
    BOOL is_pinned = (plug == p_plug);
    BOOL check_short_obj_p = (is_pinned ? pinned_plug_entry->post_short_p() : pinned_plug_entry->pre_short_p());

    // 由於這個plug的結尾被覆蓋了,下一個plug的gap是特殊gap,這裏要加回去大小
    plug_end += sizeof (gap_reloc_pair);

    //dprintf (3, ("%s %Ix is shortened, and last object %s overwritten", (is_pinned ? "PP" : "NP"), plug, (check_short_obj_p ? "is" : "is not")));
    dprintf (3, ("%s %Ix-%Ix short, LO: %s OW", (is_pinned ? "PP" : "NP"), plug, plug_end, (check_short_obj_p ? "is" : "is not")));

    verify_pins_with_post_plug_info("begin reloc short surv");

    // 枚舉plug中的對象
    while (x < plug_end)
    {
        // plug的最後一個對象被徹底覆蓋了,須要作特殊處理
        if (check_short_obj_p && ((plug_end - x) < min_pre_pin_obj_size))
        {
            dprintf (3, ("last obj %Ix is short", x));

            // 當前plug是pinned plug,結尾被下一個unpinned plug覆蓋了
            // 根據最後一個對象的成員bitmap重定位
            if (is_pinned)
            {
#ifdef COLLECTIBLE_CLASS
                if (pinned_plug_entry->post_short_collectible_p())
                    unconditional_set_card_collectible (x);
#endif //COLLECTIBLE_CLASS

                // Relocate the saved references based on bits set.
                // 成員應該存在的地址(被覆蓋的數據中),設置Card Table會使用這個地址
                uint8_t** saved_plug_info_start = (uint8_t**)(pinned_plug_entry->get_post_plug_info_start());
                // 成員真實存在的地址(備份數據中)
                uint8_t** saved_info_to_relocate = (uint8_t**)(pinned_plug_entry->get_post_plug_reloc_info());
                // 枚舉成員的bitmap
                for (size_t i = 0; i < pinned_plug_entry->get_max_short_bits(); i++)
                {
                    // 若是成員存在則重定位該成員
                    if (pinned_plug_entry->post_short_bit_p (i))
                    {
                        reloc_ref_in_shortened_obj ((saved_plug_info_start + i), (saved_info_to_relocate + i));
                    }
                }
            }
            // 當前plug是unpinned plug,結尾被下一個pinned plug覆蓋了
            // 處理和上面同樣
            else
            {
#ifdef COLLECTIBLE_CLASS
                if (pinned_plug_entry->pre_short_collectible_p())
                    unconditional_set_card_collectible (x);
#endif //COLLECTIBLE_CLASS

                relocate_pre_plug_info (pinned_plug_entry);

                // Relocate the saved references based on bits set.
                uint8_t** saved_plug_info_start = (uint8_t**)(p_plug - sizeof (plug_and_gap));
                uint8_t** saved_info_to_relocate = (uint8_t**)(pinned_plug_entry->get_pre_plug_reloc_info());
                for (size_t i = 0; i < pinned_plug_entry->get_max_short_bits(); i++)
                {
                    if (pinned_plug_entry->pre_short_bit_p (i))
                    {
                        reloc_ref_in_shortened_obj ((saved_plug_info_start + i), (saved_info_to_relocate + i));
                    }
                }
            }

            // 處理完最後一個對象,能夠跳出了
            break;
        }

        size_t s = size (x);
        uint8_t* next_obj = x + Align (s);
        Prefetch (next_obj);

        // 最後一個對象被覆蓋了,可是隻是覆蓋了後半部分,不是所有被覆蓋
        if (next_obj >= plug_end) 
        {
            dprintf (3, ("object %Ix is at the end of the plug %Ix->%Ix", 
                next_obj, plug, plug_end));

            verify_pins_with_post_plug_info("before reloc short obj");

            relocate_shortened_obj_helper (x, s, (x + Align (s) - sizeof (plug_and_gap)), pinned_plug_entry, is_pinned);
        }
        // 對象未被覆蓋,調用通常的處理
        else
        {
            relocate_obj_helper (x, s);
        }

        assert (s > 0);
        x = next_obj;
    }

    verify_pins_with_post_plug_info("end reloc short surv");
}

gc_heap::reloc_ref_in_shortened_obj函數的代碼以下:

inline 
void gc_heap::reloc_ref_in_shortened_obj (uint8_t** address_to_set_card, uint8_t** address_to_reloc)
{
    THREAD_FROM_HEAP;

    // 重定位對象
    // 這裏的address_to_reloc會在備份數據中
    uint8_t* old_val = (address_to_reloc ? *address_to_reloc : 0);
    relocate_address (address_to_reloc THREAD_NUMBER_ARG);
    if (address_to_reloc)
    {
        dprintf (3, ("SR %Ix: %Ix->%Ix", (uint8_t*)address_to_reloc, old_val, *address_to_reloc));
    }

    // 若是對象在降代範圍中,設置Card Table
    // 這裏的address_to_set_card會在被覆蓋的數據中
    //check_demotion_helper (current_saved_info_to_relocate, (uint8_t*)pval);
    uint8_t* relocated_addr = *address_to_reloc;
    if ((relocated_addr < demotion_high) &&
        (relocated_addr >= demotion_low))
    {
        dprintf (3, ("set card for location %Ix(%Ix)",
                    (size_t)address_to_set_card, card_of((uint8_t*)address_to_set_card)));

        set_card (card_of ((uint8_t*)address_to_set_card));
    }
#ifdef MULTIPLE_HEAPS
    // 不在當前heap時試着找到對象所在的heap而且用該heap處理
    else if (settings.demotion)
    {
        gc_heap* hp = heap_of (relocated_addr);
        if ((relocated_addr < hp->demotion_high) &&
            (relocated_addr >= hp->demotion_low))
        {
            dprintf (3, ("%Ix on h%d, set card for location %Ix(%Ix)",
                        relocated_addr, hp->heap_number, (size_t)address_to_set_card, card_of((uint8_t*)address_to_set_card)));

            set_card (card_of ((uint8_t*)address_to_set_card));
        }
    }
#endif //MULTIPLE_HEAPS
}

gc_heap::relocate_shortened_obj_helper函數的代碼以下:

inline
void gc_heap::relocate_shortened_obj_helper (uint8_t* x, size_t s, uint8_t* end, mark* pinned_plug_entry, BOOL is_pinned)
{
    THREAD_FROM_HEAP;
    uint8_t* plug = pinned_plug (pinned_plug_entry);

    // 若是當前plug是unpinned plug, 表明鄰接的pinned plug中保存的pre_plug_info_reloc_start可能已經被移動了
    // 這裏須要重定位pinned plug中保存的pre_plug_info_reloc_start (unpinned plug被覆蓋的內容的開始地址)
    if (!is_pinned)
    {
        //// Temporary - we just wanna make sure we are doing things right when padding is needed.
        //if ((x + s) < plug)
        //{
        //    dprintf (3, ("obj %Ix needed padding: end %Ix is %d bytes from pinned obj %Ix", 
        //        x, (x + s), (plug- (x + s)), plug));
        //    GCToOSInterface::DebugBreak();
        //}

        relocate_pre_plug_info (pinned_plug_entry);
    }

    verify_pins_with_post_plug_info("after relocate_pre_plug_info");

    uint8_t* saved_plug_info_start = 0;
    uint8_t** saved_info_to_relocate = 0;

    // saved_plug_info_start等於被覆蓋的地址的開始
    // saved_info_to_relocate等於原始內容的開始
    if (is_pinned)
    {
        saved_plug_info_start = (uint8_t*)(pinned_plug_entry->get_post_plug_info_start());
        saved_info_to_relocate = (uint8_t**)(pinned_plug_entry->get_post_plug_reloc_info());
    }
    else
    {
        saved_plug_info_start = (plug - sizeof (plug_and_gap));
        saved_info_to_relocate = (uint8_t**)(pinned_plug_entry->get_pre_plug_reloc_info());
    }
    
    uint8_t** current_saved_info_to_relocate = 0;
    uint8_t* child = 0;

    dprintf (3, ("x: %Ix, pp: %Ix, end: %Ix", x, plug, end));

    // 判斷對象中是否包含了引用
    if (contain_pointers (x))
    {
        dprintf (3,("$%Ix$", (size_t)x));

        // 重定位這個對象的全部成員
        // 注意這裏不會包含對象自身(nostart)
        go_through_object_nostart (method_table(x), x, s, pval,
        {
            dprintf (3, ("obj %Ix, member: %Ix->%Ix", x, (uint8_t*)pval, *pval));

            // 成員所在的部分被覆蓋了,調用reloc_ref_in_shortened_obj重定位
            // pval = 成員應該存在的地址(被覆蓋的數據中),設置Card Table會使用這個地址
            // current_saved_info_to_relocate = 成員真實存在的地址(備份數據中)
            if ((uint8_t*)pval >= end)
            {
                current_saved_info_to_relocate = saved_info_to_relocate + ((uint8_t*)pval - saved_plug_info_start) / sizeof (uint8_t**);
                child = *current_saved_info_to_relocate;
                reloc_ref_in_shortened_obj (pval, current_saved_info_to_relocate);
                dprintf (3, ("last part: R-%Ix(saved: %Ix)->%Ix ->%Ix",
                    (uint8_t*)pval, current_saved_info_to_relocate, child, *current_saved_info_to_relocate));
            }
            // 成員所在的部分未被覆蓋,調用通常的處理
            else
            {
                reloc_survivor_helper (pval);
            }
        });
    }

    check_class_object_demotion (x);
}

重定位階段(relocate_phase)只是修改了引用對象的地址,對象還在原來的位置,接下來進入壓縮階段(compact_phase):

壓縮階段(compact_phase)

壓縮階段負責把對象複製到以前模擬壓縮到的地址上,簡單點來說就是用memcpy複製這些對象到新的地址。
壓縮階段會使用以前構建的brick table和plug樹快速的枚舉對象。

gc_heap::compact_phase函數的代碼以下:
這個函數的代碼是否是有點眼熟?它的流程和上面的relocate_survivors很像,都是枚舉brick table而後中序枚舉plug樹

void gc_heap::compact_phase (int condemned_gen_number,
                             uint8_t*  first_condemned_address,
                             BOOL clear_cards)
{
//  %type%  category = quote (compact);
    // 統計壓縮階段的開始時間
#ifdef TIME_GC
        unsigned start;
        unsigned finish;
        start = GetCycleCount32();
#endif //TIME_GC
    generation*   condemned_gen = generation_of (condemned_gen_number);
    uint8_t*  start_address = first_condemned_address;
    size_t   current_brick = brick_of (start_address);
    heap_segment*  current_heap_segment = heap_segment_rw (generation_start_segment (condemned_gen));

    PREFIX_ASSUME(current_heap_segment != NULL);

    // 重設mark_stack_array隊列
    reset_pinned_queue_bos();

    // 更新gc_heap中的oldest_pinned_plug對象
    update_oldest_pinned_plug();

    // 若是should_expand的時候重用了之前的segment做爲ephemeral heap segment,則須要從新計算generation_allocation_size
    // reused_seg會影響壓縮參數中的check_gennum_p
    BOOL reused_seg = expand_reused_seg_p();
    if (reused_seg)
    {
        for (int i = 1; i <= max_generation; i++)
        {
            generation_allocation_size (generation_of (i)) = 0;
        }
    }

    uint8_t*  end_address = heap_segment_allocated (current_heap_segment);

    size_t  end_brick = brick_of (end_address-1);

    // 初始化壓縮參數
    compact_args args;
    // 上一個plug,用於遍歷樹時能夠從小地址到大地址遍歷(中序遍歷)
    args.last_plug = 0;
    // 當前brick的最後一個plug,更新brick table時使用
    args.before_last_plug = 0;
    // 最後設置的brick,用於複製plug後更新brick table
    args.current_compacted_brick = ~((size_t)1);
    // 當前的plug結尾是否被下一個plug覆蓋了
    args.is_shortened = FALSE;
    // last_plug或者last_plug後面的pinned plug
    // 處理plug尾部數據覆蓋時須要用到它
    args.pinned_plug_entry = 0;
    // 是否須要在複製對象時複製相應的Card Table範圍
    args.copy_cards_p =  (condemned_gen_number >= 1) || !clear_cards;
    // 從新計算generation_allocation_size時使用的參數
    args.check_gennum_p = reused_seg;
    if (args.check_gennum_p)
    {
        args.src_gennum = ((current_heap_segment == ephemeral_heap_segment) ? -1 : 2);
    }

    dprintf (2,("---- Compact Phase: %Ix(%Ix)----", 
        first_condemned_address, brick_of (first_condemned_address)));

#ifdef MULTIPLE_HEAPS
    //restart
    if (gc_t_join.joined())
    {
#endif //MULTIPLE_HEAPS

#ifdef MULTIPLE_HEAPS
        dprintf(3, ("Restarting for compaction"));
        gc_t_join.restart();
    }
#endif //MULTIPLE_HEAPS

    // 再次重設mark_stack_array隊列
    reset_pinned_queue_bos();

    // 判斷是否須要壓縮大對象的堆
#ifdef FEATURE_LOH_COMPACTION
    if (loh_compacted_p)
    {
        compact_loh();
    }
#endif //FEATURE_LOH_COMPACTION

    // 循環brick table
    if ((start_address < end_address) ||
        (condemned_gen_number == max_generation))
    {
        while (1)
        {
            // 當前segment已經處理完
            if (current_brick > end_brick)
            {
                // 處理最後一個plug,大小是heap_segment_allocated - last_plug
                if (args.last_plug != 0)
                {
                    dprintf (3, ("compacting last plug: %Ix", args.last_plug))
                    compact_plug (args.last_plug,
                                  (heap_segment_allocated (current_heap_segment) - args.last_plug),
                                  args.is_shortened,
                                  &args);
                }

                // 若是有下一個segment則處理下一個
                if (heap_segment_next_rw (current_heap_segment))
                {
                    current_heap_segment = heap_segment_next_rw (current_heap_segment);
                    current_brick = brick_of (heap_segment_mem (current_heap_segment));
                    end_brick = brick_of (heap_segment_allocated (current_heap_segment)-1);
                    args.last_plug = 0;
                    // 更新src_gennum (若是segment是ephemeral_heap_segment則須要進一步判斷)
                    if (args.check_gennum_p)
                    {
                        args.src_gennum = ((current_heap_segment == ephemeral_heap_segment) ? -1 : 2);
                    }
                    continue;
                }
                // 設置最後一個brick的偏移值, 給compact_plug善後
                else
                {
                    if (args.before_last_plug !=0)
                    {
                        dprintf (3, ("Fixing last brick %Ix to point to plug %Ix",
                                    args.current_compacted_brick, (size_t)args.before_last_plug));
                        assert (args.current_compacted_brick != ~1u);
                        set_brick (args.current_compacted_brick,
                                   args.before_last_plug - brick_address (args.current_compacted_brick));
                    }
                    break;
                }
            }
            {
                // 若是當前brick有對應的plug樹,處理當前brick
                int  brick_entry =  brick_table [ current_brick ];
                dprintf (3, ("B: %Ix(%Ix)->%Ix", 
                    current_brick, (size_t)brick_entry, (brick_address (current_brick) + brick_entry - 1)));

                if (brick_entry >= 0)
                {
                    compact_in_brick ((brick_address (current_brick) + brick_entry -1),
                                      &args);

                }
            }
            current_brick++;
        }
    }

    // 複製已完畢
    // 恢復備份的數據到被覆蓋的部分
    recover_saved_pinned_info();

    // 統計壓縮階段的結束時間
#ifdef TIME_GC
    finish = GetCycleCount32();
    compact_time = finish - start;
#endif //TIME_GC

    concurrent_print_time_delta ("compact end");

    dprintf(2,("---- End of Compact phase ----"));
}

gc_heap::compact_in_brick函數的代碼以下:
這個函數和上面的relocate_survivors_in_brick函數很像

void gc_heap::compact_in_brick (uint8_t* tree, compact_args* args)
{
    assert (tree != NULL);
    int   left_node = node_left_child (tree);
    int   right_node = node_right_child (tree);
    // 須要移動的偏移值,前面計劃階段模擬壓縮時設置的reloc
    ptrdiff_t relocation = node_relocation_distance (tree);

    args->print();

    // 處理左節點
    if (left_node)
    {
        dprintf (3, ("B: L: %d->%Ix", left_node, (tree + left_node)));
        compact_in_brick ((tree + left_node), args);
    }

    uint8_t*  plug = tree;
    BOOL   has_pre_plug_info_p = FALSE;
    BOOL   has_post_plug_info_p = FALSE;

    // 若是這個plug是pinned plug
    // 獲取是否有has_pre_plug_info_p (是否覆蓋了last_plug的尾部)
    // 獲取是否有has_post_plug_info_p (是否被下一個plug覆蓋了尾部)
    if (tree == oldest_pinned_plug)
    {
        args->pinned_plug_entry = get_oldest_pinned_entry (&has_pre_plug_info_p,
                                                           &has_post_plug_info_p);
        assert (tree == pinned_plug (args->pinned_plug_entry));
    }

    // 處理last_plug
    if (args->last_plug != 0)
    {
        size_t gap_size = node_gap_size (tree);
        // last_plug的結尾 = 當前plug的開始地址 - gap
        uint8_t*  gap = (plug - gap_size);
        uint8_t*  last_plug_end = gap;
        // last_plug的大小 = last_plug的結尾 - last_plug的開始
        size_t last_plug_size = (last_plug_end - args->last_plug);
        dprintf (3, ("tree: %Ix, last_plug: %Ix, gap: %Ix(%Ix), last_plug_end: %Ix, size: %Ix", 
            tree, args->last_plug, gap, gap_size, last_plug_end, last_plug_size));
        
        // last_plug的尾部是否被覆蓋了
        // args->is_shortened表明last_plug是pinned_plug,被下一個unpinned plug覆蓋了尾部
        // has_pre_plug_info_p表明last_plug是unpinned plug,被下一個pinned plug覆蓋了尾部
        BOOL check_last_object_p = (args->is_shortened || has_pre_plug_info_p);
        if (!check_last_object_p)
        {
            assert (last_plug_size >= Align (min_obj_size));
        }

        // 處理last_plug
        compact_plug (args->last_plug, last_plug_size, check_last_object_p, args);
    }
    else
    {
        // 第一個plug不可能覆蓋前面的plug的結尾
        assert (!has_pre_plug_info_p);
    }

    dprintf (3, ("set args last plug to plug: %Ix, reloc: %Ix", plug, relocation));
    // 設置last_plug
    args->last_plug = plug;
    // 設置last_plugd移動偏移值
    args->last_plug_relocation = relocation;
    // 設置是否被覆蓋了尾部
    args->is_shortened = has_post_plug_info_p;

    // 處理右節點
    if (right_node)
    {
        dprintf (3, ("B: R: %d->%Ix", right_node, (tree + right_node)));
        compact_in_brick ((tree + right_node), args);
    }
}

gc_heap::compact_plug函數的代碼以下:

void gc_heap::compact_plug (uint8_t* plug, size_t size, BOOL check_last_object_p, compact_args* args)
{
    args->print();

    // 複製到的地址,plug + reloc
    uint8_t* reloc_plug = plug + args->last_plug_relocation;

    // 若是plug的結尾被覆蓋過
    if (check_last_object_p)
    {
        // 添加特殊gap的大小
        size += sizeof (gap_reloc_pair);
        mark* entry = args->pinned_plug_entry;

        // 在複製內存前把被覆蓋的內容和原始內容交換一下
        // 複製內存後須要交換回去
        if (args->is_shortened)
        {
            // 當前plug是pinned plug,被下一個unpinned plug覆蓋
            assert (entry->has_post_plug_info());
            entry->swap_post_plug_and_saved();
        }
        else
        {
            // 當前plug是unpinned plug,被下一個pinned plug覆蓋
            assert (entry->has_pre_plug_info());
            entry->swap_pre_plug_and_saved();
        }
    }

    // 複製以前的brick中的偏移值
    int  old_brick_entry =  brick_table [brick_of (plug)];

    assert (node_relocation_distance (plug) == args->last_plug_relocation);

    // 處理對齊和pad
#ifdef FEATURE_STRUCTALIGN
    ptrdiff_t alignpad = node_alignpad(plug);
    if (alignpad)
    {
        make_unused_array (reloc_plug - alignpad, alignpad);
        if (brick_of (reloc_plug - alignpad) != brick_of (reloc_plug))
        {
            // The alignment padding is straddling one or more bricks;
            // it has to be the last "object" of its first brick.
            fix_brick_to_highest (reloc_plug - alignpad, reloc_plug);
        }
    }
#else // FEATURE_STRUCTALIGN
    size_t unused_arr_size = 0; 
    BOOL  already_padded_p = FALSE;
#ifdef SHORT_PLUGS
    if (is_plug_padded (plug))
    {
        already_padded_p = TRUE;
        clear_plug_padded (plug);
        unused_arr_size = Align (min_obj_size);
    }
#endif //SHORT_PLUGS
    if (node_realigned (plug))
    {
        unused_arr_size += switch_alignment_size (already_padded_p);
    }

    if (unused_arr_size != 0) 
    {
        make_unused_array (reloc_plug - unused_arr_size, unused_arr_size);

        if (brick_of (reloc_plug - unused_arr_size) != brick_of (reloc_plug))
        {
            dprintf (3, ("fix B for padding: %Id: %Ix->%Ix", 
                unused_arr_size, (reloc_plug - unused_arr_size), reloc_plug));
            // The alignment padding is straddling one or more bricks;
            // it has to be the last "object" of its first brick.
            fix_brick_to_highest (reloc_plug - unused_arr_size, reloc_plug);
        }
    }
#endif // FEATURE_STRUCTALIGN

#ifdef SHORT_PLUGS
    if (is_plug_padded (plug))
    {
        make_unused_array (reloc_plug - Align (min_obj_size), Align (min_obj_size));

        if (brick_of (reloc_plug - Align (min_obj_size)) != brick_of (reloc_plug))
        {
            // The alignment padding is straddling one or more bricks;
            // it has to be the last "object" of its first brick.
            fix_brick_to_highest (reloc_plug - Align (min_obj_size), reloc_plug);
        }
    }
#endif //SHORT_PLUGS

    // 複製plug中的全部內容和對應的Card Table中的範圍(若是copy_cards_p成立)
    gcmemcopy (reloc_plug, plug, size, args->copy_cards_p);

    // 從新統計generation_allocation_size
    if (args->check_gennum_p)
    {
        int src_gennum = args->src_gennum;
        if (src_gennum == -1)
        {
            src_gennum = object_gennum (plug);
        }

        int dest_gennum = object_gennum_plan (reloc_plug);

        if (src_gennum < dest_gennum)
        {
            generation_allocation_size (generation_of (dest_gennum)) += size;
        }
    }

    // 更新brick table
    // brick table中會保存brick的最後一個plug的偏移值,跨越多個brick的時候後面的brick會是-1
    size_t current_reloc_brick = args->current_compacted_brick;

    // 若是已經到了下一個brick
    // 設置上一個brick的值 = 上一個brick中最後的plug的偏移值, 或者-1
    if (brick_of (reloc_plug) != current_reloc_brick)
    {
        dprintf (3, ("last reloc B: %Ix, current reloc B: %Ix", 
            current_reloc_brick, brick_of (reloc_plug)));

        if (args->before_last_plug)
        {
            dprintf (3,(" fixing last brick %Ix to point to last plug %Ix(%Ix)",
                     current_reloc_brick,
                     args->before_last_plug, 
                     (args->before_last_plug - brick_address (current_reloc_brick))));

            {
                set_brick (current_reloc_brick,
                        args->before_last_plug - brick_address (current_reloc_brick));
            }
        }
        current_reloc_brick = brick_of (reloc_plug);
    }
    
    // 若是跨越了多個brick
    size_t end_brick = brick_of (reloc_plug + size-1);
    if (end_brick != current_reloc_brick)
    {
        // The plug is straddling one or more bricks
        // It has to be the last plug of its first brick
        dprintf (3,("plug spanning multiple bricks, fixing first brick %Ix to %Ix(%Ix)",
                 current_reloc_brick, (size_t)reloc_plug,
                 (reloc_plug - brick_address (current_reloc_brick))));

        // 設置第一個brick中的偏移值
        {
            set_brick (current_reloc_brick,
                    reloc_plug - brick_address (current_reloc_brick));
        }

        // 把後面的brick設爲-1,除了end_brick
        // update all intervening brick
        size_t brick = current_reloc_brick + 1;
        dprintf (3,("setting intervening bricks %Ix->%Ix to -1",
            brick, (end_brick - 1)));
        while (brick < end_brick)
        {
            set_brick (brick, -1);
            brick++;
        }

        // 若是end_brick中無其餘plug,end_brick也會被設爲-1
        // brick_address (end_brick) - 1 - brick_address (end_brick) = -1
        // code last brick offset as a plug address
        args->before_last_plug = brick_address (end_brick) -1;
        current_reloc_brick = end_brick;
        dprintf (3, ("setting before last to %Ix, last brick to %Ix",
            args->before_last_plug, current_reloc_brick));
    } 
    // 若是隻在一個brick中
    else
    {
        // 記錄當前brick中的最後一個plug
        dprintf (3, ("still in the same brick: %Ix", end_brick));
        args->before_last_plug = reloc_plug;
    }
    // 更新最後設置的brick
    args->current_compacted_brick = current_reloc_brick;

    // 複製完畢之後把被覆蓋的內容和原始內容交換回去
    // 注意若是plug移動的距離比覆蓋的大小要少,這裏會把複製後的內容給破壞掉
    // 後面還須要使用recover_saved_pinned_info還原
    if (check_last_object_p)
    {
        mark* entry = args->pinned_plug_entry;

        if (args->is_shortened)
        {
            entry->swap_post_plug_and_saved();
        }
        else
        {
            entry->swap_pre_plug_and_saved();
        }
    }
}

gc_heap::gcmemcopy函數的代碼以下:

// POPO TODO: We should actually just recover the artifically made gaps here..because when we copy
// we always copy the earlier plugs first which means we won't need the gap sizes anymore. This way
// we won't need to individually recover each overwritten part of plugs.
inline
void  gc_heap::gcmemcopy (uint8_t* dest, uint8_t* src, size_t len, BOOL copy_cards_p)
{
    // 若是地址同樣能夠跳過
    if (dest != src)
    {
#ifdef BACKGROUND_GC
        if (current_c_gc_state == c_gc_state_marking) 
        {
            //TODO: should look to see whether we should consider changing this
            // to copy a consecutive region of the mark array instead.
            copy_mark_bits_for_addresses (dest, src, len);
        }
#endif //BACKGROUND_GC
        // 複製plug中的全部對象到新的地址上
        // memcopy作的東西和memcpy同樣,微軟本身寫的一個函數而已
        //dprintf(3,(" Memcopy [%Ix->%Ix, %Ix->%Ix[", (size_t)src, (size_t)dest, (size_t)src+len, (size_t)dest+len));
        dprintf(3,(" mc: [%Ix->%Ix, %Ix->%Ix[", (size_t)src, (size_t)dest, (size_t)src+len, (size_t)dest+len));
        memcopy (dest - plug_skew, src - plug_skew, (int)len);
#ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
        if (SoftwareWriteWatch::IsEnabledForGCHeap())
        {
            // The ranges [src - plug_kew .. src[ and [src + len - plug_skew .. src + len[ are ObjHeaders, which don't have GC
            // references, and are not relevant for write watch. The latter range actually corresponds to the ObjHeader for the
            // object at (src + len), so it can be ignored anyway.
            SoftwareWriteWatch::SetDirtyRegion(dest, len - plug_skew);
        }
#endif // FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
        // 複製對應的Card Table範圍
        // copy_cards_p成立的時候複製src ~ src+len到dest
        // copy_cards_p不成立的時候清除dest ~ dest+len
        copy_cards_range (dest, src, len, copy_cards_p);
    }
}

gc_heap::compact_loh函數的代碼以下:

void gc_heap::compact_loh()
{
    assert (should_compact_loh());

    generation* gen        = large_object_generation;
    heap_segment* start_seg = heap_segment_rw (generation_start_segment (gen));
    PREFIX_ASSUME(start_seg != NULL);
    heap_segment* seg      = start_seg;
    heap_segment* prev_seg = 0;
    uint8_t* o             = generation_allocation_start (gen);

    //Skip the generation gap object
    o = o + AlignQword (size (o));
    // We don't need to ever realloc gen3 start so don't touch it.
    uint8_t* free_space_start = o;
    uint8_t* free_space_end = o;
    generation_allocator (gen)->clear();
    generation_free_list_space (gen) = 0;
    generation_free_obj_space (gen) = 0;

    loh_pinned_queue_bos = 0;

    // 枚舉大對象的堆
    while (1)
    {
        // 當前segment處理完畢,處理下一個
        if (o >= heap_segment_allocated (seg))
        {
            heap_segment* next_seg = heap_segment_next (seg);

            // 若是當前segment爲空,表示能夠刪掉這個segment
            // 修改segment鏈表,把空的segment放到後面
            if ((heap_segment_plan_allocated (seg) == heap_segment_mem (seg)) &&
                (seg != start_seg) && !heap_segment_read_only_p (seg))
            {
                dprintf (3, ("Preparing empty large segment %Ix", (size_t)seg));
                assert (prev_seg);
                heap_segment_next (prev_seg) = next_seg;
                heap_segment_next (seg) = freeable_large_heap_segment;
                freeable_large_heap_segment = seg;
            }
            else
            {
                // 更新heap_segment_allocated
                // 釋放(decommit)未使用的內存空間
                if (!heap_segment_read_only_p (seg))
                {
                    // We grew the segment to accommondate allocations.
                    if (heap_segment_plan_allocated (seg) > heap_segment_allocated (seg))
                    {
                        if ((heap_segment_plan_allocated (seg) - plug_skew)  > heap_segment_used (seg))
                        {
                            heap_segment_used (seg) = heap_segment_plan_allocated (seg) - plug_skew;
                        }
                    }

                    heap_segment_allocated (seg) = heap_segment_plan_allocated (seg);
                    dprintf (3, ("Trimming seg to %Ix[", heap_segment_allocated (seg)));
                    decommit_heap_segment_pages (seg, 0);
                    dprintf (1236, ("CLOH: seg: %Ix, alloc: %Ix, used: %Ix, committed: %Ix",
                        seg, 
                        heap_segment_allocated (seg),
                        heap_segment_used (seg),
                        heap_segment_committed (seg)));
                    //heap_segment_used (seg) = heap_segment_allocated (seg) - plug_skew;
                    dprintf (1236, ("CLOH: used is set to %Ix", heap_segment_used (seg)));
                }
                prev_seg = seg;
            }

            // 處理下一個segment,不存在時跳出
            seg = next_seg;
            if (seg == 0)
                break;
            else
            {
                o = heap_segment_mem (seg);
            }
        }

        // 若是對象已標記
        if (marked (o))
        {
            free_space_end = o;
            size_t size = AlignQword (size (o));

            size_t loh_pad;
            uint8_t* reloc = o;
            // 清除標記
            clear_marked (o);

            // 若是對象是固定的
            if (pinned (o))
            {
                // We are relying on the fact the pinned objects are always looked at in the same order 
                // in plan phase and in compact phase.
                mark* m = loh_pinned_plug_of (loh_deque_pinned_plug());
                uint8_t* plug = pinned_plug (m);
                assert (plug == o);

                loh_pad = pinned_len (m);
                // 清除固定標記
                clear_pinned (o);
            }
            else
            {
                loh_pad = AlignQword (loh_padding_obj_size);

                // 複製對象內存
                reloc += loh_node_relocation_distance (o);
                gcmemcopy (reloc, o, size, TRUE);
            }

            // 添加loh_pad到free list
            thread_gap ((reloc - loh_pad), loh_pad, gen);

            // 處理下一個對象
            o = o + size;
            free_space_start = o;
            if (o < heap_segment_allocated (seg))
            {
                assert (!marked (o));
            }
        }
        else
        {
            // 跳過未標記對象
            while (o < heap_segment_allocated (seg) && !marked (o))
            {
                o = o + AlignQword (size (o));
            }
        }
    }

    assert (loh_pinned_plug_que_empty_p());

    dprintf (1235, ("after GC LOH size: %Id, free list: %Id, free obj: %Id\n\n", 
        generation_size (max_generation + 1), 
        generation_free_list_space (gen),
        generation_free_obj_space (gen)));
}

gc_heap::recover_saved_pinned_info函數的代碼以下:

void gc_heap::recover_saved_pinned_info()
{
    // 重設mark_stack_array隊列
    reset_pinned_queue_bos();

    // 恢復各個pinned plug被覆蓋或者覆蓋的數據
    while (!(pinned_plug_que_empty_p()))
    {
        mark* oldest_entry = oldest_pin();
        oldest_entry->recover_plug_info();
#ifdef GC_CONFIG_DRIVEN
        if (oldest_entry->has_pre_plug_info() && oldest_entry->has_post_plug_info())
            record_interesting_data_point (idp_pre_and_post_pin);
        else if (oldest_entry->has_pre_plug_info())
            record_interesting_data_point (idp_pre_pin);
        else if (oldest_entry->has_post_plug_info())
            record_interesting_data_point (idp_post_pin);
#endif //GC_CONFIG_DRIVEN

        deque_pinned_plug();
    }
}

mark::recover_plug_info函數的代碼以下:
函數前面的註釋講的是以前複製plug的時候已經包含了被覆蓋的內容(swap_pre_plug_and_saved),
可是若是移動的位置小於3個指針的大小(註釋中的< 3應該是>= 3)則複製完之後有可能再次被swap_pre_plug_and_saved破壞掉。

// We should think about whether it's really necessary to have to copy back the pre plug
// info since it was already copied during compacting plugs. But if a plug doesn't move
// by < 3 ptr size, it means we'd have to recover pre plug info.
void recover_plug_info() 
{
    // 若是這個pinned plug覆蓋了前一個unpinned plug的結尾,把備份的數據恢復回去
    if (saved_pre_p)
    {
        // 若是已經壓縮過,須要複製到重定位後的saved_pre_plug_info_reloc_start
        // 而且使用saved_pre_plug_reloc備份(這個備份裏面的成員也通過了重定位)
        if (gc_heap::settings.compaction)
        {
            dprintf (3, ("%Ix: REC Pre: %Ix-%Ix", 
                first,
                &saved_pre_plug_reloc, 
                saved_pre_plug_info_reloc_start));
            memcpy (saved_pre_plug_info_reloc_start, &saved_pre_plug_reloc, sizeof (saved_pre_plug_reloc));
        }
        // 若是未壓縮過,能夠複製到這個pinned plug的前面
        // 而且使用saved_pre_plug備份
        else
        {
            dprintf (3, ("%Ix: REC Pre: %Ix-%Ix", 
                first,
                &saved_pre_plug, 
                (first - sizeof (plug_and_gap))));
            memcpy ((first - sizeof (plug_and_gap)), &saved_pre_plug, sizeof (saved_pre_plug));
        }
    }

    // 若是這個pinned plug被下一個unpinned plug覆蓋告終尾,把備份的數據恢復回去
    if (saved_post_p)
    {
        // 由於pinned plug不會移動
        // 這裏的saved_post_plug_info_start不會改變
        // 使用saved_post_plug_reloc備份(這個備份裏面的成員也通過了重定位)
        if (gc_heap::settings.compaction)
        {
            dprintf (3, ("%Ix: REC Post: %Ix-%Ix", 
                first,
                &saved_post_plug_reloc, 
                saved_post_plug_info_start));
            memcpy (saved_post_plug_info_start, &saved_post_plug_reloc, sizeof (saved_post_plug_reloc));
        }
        // 使用saved_pre_plug備份
        else
        {
            dprintf (3, ("%Ix: REC Post: %Ix-%Ix", 
                first,
                &saved_post_plug, 
                saved_post_plug_info_start));
            memcpy (saved_post_plug_info_start, &saved_post_plug, sizeof (saved_post_plug));
        }
    }
}

壓縮階段結束之後還須要作一些收尾工做,請從上面plan_phase中的fix_generation_bounds (condemned_gen_number, consing_gen);繼續看。
若是計劃階段不選擇壓縮,就會進入清掃階段:

清掃階段(sweep_phase)

清掃階段負責把plug與plug之間的空間變爲free object而後加到對應代的free list中,而且負責修改代邊界。
加到free list中的區域會在後面供分配新的上下文使用。

清掃階段的主要工做在函數make_free_lists中完成,名稱叫sweep_phase的函數目前不存在。
掃描plug時會使用計劃階段構建好的plug信息和brick table,但模擬壓縮的偏移值reloc和計劃代邊界plan_allocation_start不會被使用。

清掃階段的代碼

gc_heap::make_free_lists函數的代碼以下:

void gc_heap::make_free_lists (int condemned_gen_number)
{
    // 統計清掃階段的開始時間
#ifdef TIME_GC
    unsigned start;
    unsigned finish;
    start = GetCycleCount32();
#endif //TIME_GC

    //Promotion has to happen in sweep case.
    assert (settings.promotion);

    // 從收集代的第一個segment開始處理
    generation* condemned_gen = generation_of (condemned_gen_number);
    uint8_t* start_address = generation_allocation_start (condemned_gen);

    size_t  current_brick = brick_of (start_address);
    heap_segment* current_heap_segment = heap_segment_rw (generation_start_segment (condemned_gen));

    PREFIX_ASSUME(current_heap_segment != NULL);

    uint8_t*  end_address = heap_segment_allocated (current_heap_segment);
    size_t  end_brick = brick_of (end_address-1);

    // 清掃階段使用的參數
    make_free_args args;
    // 當前生成的free object應該歸到的代序號
    // 更新代邊界的時候也會使用
    args.free_list_gen_number = min (max_generation, 1 + condemned_gen_number);
    // 超過這個值就須要更新free_list_gen_number和free_list_gen
    // 在清掃階段settings.promotion == true時
    //    generation_limit遇到gen 0或者gen 1的時候返回heap_segment_reserved (ephemeral_heap_segment),則原代0的對象歸到代1
    //    generation_limit遇到gen 2的時候返回generation_allocation_start (generation_of ((gen_number - 2))),則原代1的對象歸到代2
    // MAX_PTR只是用來檢測第一次使用的,後面會更新
    args.current_gen_limit = (((condemned_gen_number == max_generation)) ?
                              MAX_PTR :
                              (generation_limit (args.free_list_gen_number)));
    // 當前生成的free object應該歸到的代
    args.free_list_gen = generation_of (args.free_list_gen_number);
    // 當前brick中地址最大的plug,用於更新brick表
    args.highest_plug = 0;

    // 開始遍歷brick
    if ((start_address < end_address) ||
        (condemned_gen_number == max_generation))
    {
        while (1)
        {
            // 當前segment處理完畢
            if ((current_brick > end_brick))
            {
                // 若是第一個segment無存活的對象,則重設它的heap_segment_allocated
                // 而且設置generation_allocation_start (gen)等於這個空segment的開始地址
                if (args.current_gen_limit == MAX_PTR)
                {
                    //We had an empty segment
                    //need to allocate the generation start

                    generation* gen = generation_of (max_generation);

                    heap_segment* start_seg = heap_segment_rw (generation_start_segment (gen));

                    PREFIX_ASSUME(start_seg != NULL);

                    uint8_t* gap = heap_segment_mem (start_seg);

                    generation_allocation_start (gen) = gap;
                    heap_segment_allocated (start_seg) = gap + Align (min_obj_size);
                    // 確保代最少有一個對象
                    make_unused_array (gap, Align (min_obj_size));
                    // 更新代邊界
                    reset_allocation_pointers (gen, gap);
                    dprintf (3, ("Start segment empty, fixing generation start of %d to: %Ix",
                                 max_generation, (size_t)gap));
                    // 更新current_gen_limit
                    args.current_gen_limit = generation_limit (args.free_list_gen_number);
                }
                // 有下一個segment的時候繼續處理下一個segment, 不然跳出
                if (heap_segment_next_rw (current_heap_segment))
                {
                    current_heap_segment = heap_segment_next_rw (current_heap_segment);
                    current_brick = brick_of (heap_segment_mem (current_heap_segment));
                    end_brick = brick_of (heap_segment_allocated (current_heap_segment)-1);

                    continue;
                }
                else
                {
                    break;
                }
            }
            {
                // 若是brick中保存了對plug樹的偏移值則
                //    調用make_free_list_in_brick
                //    設置brick到地址最大的plug
                // 不然設置設爲-1 (把-2, -3等等的都改成-1)
                int brick_entry =  brick_table [ current_brick ];
                if ((brick_entry >= 0))
                {
                    make_free_list_in_brick (brick_address (current_brick) + brick_entry-1, &args);
                    dprintf(3,("Fixing brick entry %Ix to %Ix",
                               current_brick, (size_t)args.highest_plug));
                    set_brick (current_brick,
                               (args.highest_plug - brick_address (current_brick)));
                }
                else
                {
                    if ((brick_entry > -32768))
                    {

#ifdef _DEBUG
                        ptrdiff_t offset = brick_of (args.highest_plug) - current_brick;
                        if ((brick_entry != -32767) && (! ((offset == brick_entry))))
                        {
                            assert ((brick_entry == -1));
                        }
#endif //_DEBUG
                        //init to -1 for faster find_first_object
                        set_brick (current_brick, -1);
                    }
                }
            }
            current_brick++;
        }
    }
    {
        // 設置剩餘的代邊界
        int bottom_gen = 0;
        args.free_list_gen_number--;
        while (args.free_list_gen_number >= bottom_gen)
        {
            uint8_t*  gap = 0;
            generation* gen2 = generation_of (args.free_list_gen_number);
            // 保證代中最少有一個對象
            gap = allocate_at_end (Align(min_obj_size));
            generation_allocation_start (gen2) = gap;
            // 設置代邊界
            reset_allocation_pointers (gen2, gap);
            dprintf(3,("Fixing generation start of %d to: %Ix",
                       args.free_list_gen_number, (size_t)gap));
            PREFIX_ASSUME(gap != NULL);
            // 代中第一個對象應該是free object
            make_unused_array (gap, Align (min_obj_size));

            args.free_list_gen_number--;
        }

        // 更新alloc_allocated成員到gen 0的開始邊界
        //reset the allocated size
        uint8_t* start2 = generation_allocation_start (youngest_generation);
        alloc_allocated = start2 + Align (size (start2));
    }

    // 統計清掃階段的結束時間
#ifdef TIME_GC
    finish = GetCycleCount32();
    sweep_time = finish - start;
#endif //TIME_GC
}

gc_heap::make_free_list_in_brick函數的代碼以下:

void gc_heap::make_free_list_in_brick (uint8_t* tree, make_free_args* args)
{
    assert ((tree != NULL));
    {
        int  right_node = node_right_child (tree);
        int left_node = node_left_child (tree);
        args->highest_plug = 0;
        if (! (0 == tree))
        {
            // 處理左邊的節點
            if (! (0 == left_node))
            {
                make_free_list_in_brick (tree + left_node, args);
            }
            // 處理當前節點
            {
                uint8_t*  plug = tree;
                // 當前plug前面的空餘空間
                size_t  gap_size = node_gap_size (tree);
                // 空餘空間的開始
                uint8_t*  gap = (plug - gap_size);
                dprintf (3,("Making free list %Ix len %d in %d",
                //dprintf (3,("F: %Ix len %Ix in %d",
                        (size_t)gap, gap_size, args->free_list_gen_number));
                // 記錄當前brick中地址最大的plug
                args->highest_plug = tree;
#ifdef SHORT_PLUGS
                if (is_plug_padded (plug))
                {
                    dprintf (3, ("%Ix padded", plug));
                    clear_plug_padded (plug);
                }
#endif //SHORT_PLUGS
            gen_crossing:
                {
                    // 若是current_gen_limit等於MAX_PTR,表示咱們須要先決定gen 2的邊界
                    // 若是plug >= args->current_gen_limit而且plug在ephemeral heap segment,表示咱們須要決定gen 1或gen 0的邊界
                    // 決定的流程以下
                    // - 第一次current_gen_limit == MAX_PTR,在處理全部對象以前決定gen 2的邊界
                    // - 第二次plug超過了generation_allocation_start (generation_of ((gen_number - 2)))而且在ephemeral heap segment中,決定gen 1的邊界
                    // - 由於plug不會超過heap_segment_reserved (ephemeral_heap_segment),第三次會在上面的"設置剩餘的代邊界"中決定gen 0的邊界
                    if ((args->current_gen_limit == MAX_PTR) ||
                        ((plug >= args->current_gen_limit) &&
                         ephemeral_pointer_p (plug)))
                    {
                        dprintf(3,(" Crossing Generation boundary at %Ix",
                               (size_t)args->current_gen_limit));
                        // 在處理全部對象以前決定gen 2的邊界時,不須要減1
                        if (!(args->current_gen_limit == MAX_PTR))
                        {
                            args->free_list_gen_number--;
                            args->free_list_gen = generation_of (args->free_list_gen_number);
                        }
                        dprintf(3,( " Fixing generation start of %d to: %Ix",
                                args->free_list_gen_number, (size_t)gap));
                        
                        // 決定代邊界
                        reset_allocation_pointers (args->free_list_gen, gap);
                        // 更新current_gen_limit用於決定下一個代的邊界
                        args->current_gen_limit = generation_limit (args->free_list_gen_number);

                        // 保證代中最少有一個對象
                        // 若是這個gap比較大(大於最小對象大小 * 2),剩餘的空間還能夠在下面放到free list中
                        if ((gap_size >= (2*Align (min_obj_size))))
                        {
                            dprintf(3,(" Splitting the gap in two %Id left",
                                   gap_size));
                            make_unused_array (gap, Align(min_obj_size));
                            gap_size = (gap_size - Align(min_obj_size));
                            gap = (gap + Align(min_obj_size));
                        }
                        else
                        {
                            make_unused_array (gap, gap_size);
                            gap_size = 0;
                        }
                        goto gen_crossing;
                    }
                }

                // 加到free list中
                thread_gap (gap, gap_size, args->free_list_gen);
                add_gen_free (args->free_list_gen->gen_num, gap_size);
            }
            // 處理右邊的節點
            if (! (0 == right_node))
            {
                make_free_list_in_brick (tree + right_node, args);
            }
        }
    }
}

壓縮階段結束之後還須要作一些收尾工做,請從上面plan_phase中的recover_saved_pinned_info();繼續看。

參考連接

https://github.com/dotnet/coreclr/blob/master/Documentation/botr/garbage-collection.md
https://raw.githubusercontent.com/dotnet/coreclr/release/1.1.0/src/gc/gc.cpp
https://github.com/dotnet/coreclr/blob/release/1.1.0/src/gc/gcimpl.h
https://github.com/dotnet/coreclr/blob/release/1.1.0/src/gc/gcpriv.h
https://github.com/dotnet/coreclr/issues/8959
https://github.com/dotnet/coreclr/issues/8995
https://github.com/dotnet/coreclr/issues/9053
https://github.com/dotnet/coreclr/issues/10137
https://github.com/dotnet/coreclr/issues/10305
https://github.com/dotnet/coreclr/issues/10141

寫在最後

GC的實際處理遠遠比文檔和書中寫的要複雜,但願這一篇文章可讓你更加深刻的理解CoreCLR,若是你發現了錯誤或者有疑問的地方請指出來,
另外這篇文章有一些部分還沒有涵蓋到,例如SuspendEE的原理,後臺GC的處理和stackwalking等,但願之後能夠再花時間去研究它們。

下一篇我將會實際使用LLDB跟蹤GC收集垃圾的處理,再下一篇會寫JIT相關的內容,敬請期待。

相關文章
相關標籤/搜索