GC析構物件和列表的處理過程

tangyanzhi1111發表於2020-10-27

步驟如下:
1.CLR 會判斷當前物件是否包含解構函式,
2.如果包含,則記錄物件的地址到所屬區域下的析構物件列表中
3.在CLR GC的五個階段的標記階段,會判斷物件是否存活為1
4.如果不為1,檢查物件頭中的一直執行解構函式標記是否為1
5.如果為1,則刪除析構物件列表的中的記錄
6.如果不為1,則把析構物件列表中的記錄移動到析構佇列或者重要析構佇列
7.標記析構佇列和重要析構佇列的物件為存活物件,即為1
8.等待被解構函式的呼叫執行緒呼叫它
9.移除析構佇列或者重要析構佇列
10.下一輪GC的回收

**(一)**管理以上的類CFinalize
.Net裡面有些物件包含了解構函式的處理, CLR為了保證這些析構物件的呼叫以及不影響主體GC的分配與回收,在GC_Heap裡面定義了管理析構物件的CFinalize類

class CFinalize
{
#ifdef DACCESS_COMPILE
    friend class ::ClrDataAccess;
#endif // DACCESS_COMPILE

    friend class CFinalizeStaticAsserts;

private:

    //adjust the count and add a constant to add a segment
    static const int ExtraSegCount = 2;
    static const int FinalizerListSeg = NUMBERGENERATIONS+1;
    static const int CriticalFinalizerListSeg = NUMBERGENERATIONS;
    //Does not correspond to a segment
    static const int FreeList = NUMBERGENERATIONS+ExtraSegCount;

    PTR_PTR_Object m_FillPointers[NUMBERGENERATIONS+ExtraSegCount];
    PTR_PTR_Object m_Array;
    PTR_PTR_Object m_EndArray;
    size_t   m_PromotedCount;
    
    VOLATILE(int32_t) lock;
#ifdef _DEBUG
    EEThreadId lockowner_threadid;
#endif // _DEBUG

    BOOL GrowArray();
    void MoveItem (Object** fromIndex,
                   unsigned int fromSeg,
                   unsigned int toSeg);

    inline PTR_PTR_Object& SegQueue (unsigned int Seg)
    {
        return (Seg ? m_FillPointers [Seg-1] : m_Array);
    }
    inline PTR_PTR_Object& SegQueueLimit (unsigned int Seg)
    {
        return m_FillPointers [Seg];
    }

    BOOL IsSegEmpty ( unsigned int i)
    {
        ASSERT ( (int)i < FreeList);
        return (SegQueueLimit(i) == SegQueue (i));

    }

    BOOL FinalizeSegForAppDomain (AppDomain *pDomain, 
                                  BOOL fRunFinalizers, 
                                  unsigned int Seg);

public:
    ~CFinalize();
    bool Initialize();
    void EnterFinalizeLock();
    void LeaveFinalizeLock();
    bool RegisterForFinalization (int gen, Object* obj, size_t size=0);
    Object* GetNextFinalizableObject (BOOL only_non_critical=FALSE);
    BOOL ScanForFinalization (promote_func* fn, int gen,BOOL mark_only_p, gc_heap* hp);
    void RelocateFinalizationData (int gen, gc_heap* hp);
    void WalkFReachableObjects (fq_walk_fn fn);
    void GcScanRoots (promote_func* fn, int hn, ScanContext *pSC);
    void UpdatePromotedGenerations (int gen, BOOL gen_0_empty_p);
    size_t GetPromotedCount();

    //Methods used by the shutdown code to call every finalizer
    void SetSegForShutDown(BOOL fHasLock);
    size_t GetNumberFinalizableObjects();
    void DiscardNonCriticalObjects();

    //Methods used by the app domain unloading call to finalize objects in an app domain
    bool FinalizeAppDomain (AppDomain *pDomain, bool fRunFinalizers);

    void CheckFinalizerObjects();

};

class CFinalizeStaticAsserts {
    static_assert(dac_finalize_queue::ExtraSegCount == CFinalize::ExtraSegCount, "ExtraSegCount mismatch");
    static_assert(offsetof(dac_finalize_queue, m_FillPointers) == offsetof(CFinalize, m_FillPointers), "CFinalize layout mismatch");
};

(二)新增到析構物件列表

CFinalize::RegisterForFinalization (int gen, Object* obj, size_t size)
{
    CONTRACTL {
        NOTHROW;
        GC_NOTRIGGER;
    } CONTRACTL_END;

    EnterFinalizeLock();
    // Adjust gen
    unsigned int dest = 0;

    if (g_fFinalizerRunOnShutDown)
    {
        //no method table available yet,
        //put it in the finalizer queue and sort out when
        //dequeueing
        dest = FinalizerListSeg;
    }

    else
        dest = gen_segment (gen);

    // Adjust boundary for segments so that GC will keep objects alive.
    Object*** s_i = &SegQueue (FreeList);
    if ((*s_i) == m_EndArray)
    {
        if (!GrowArray())
        {
            LeaveFinalizeLock();
            if (method_table(obj) == NULL)
            {
                // If the object is uninitialized, a valid size should have been passed.
                assert (size >= Align (min_obj_size));
                dprintf (3, ("Making unused array [%Ix, %Ix[", (size_t)obj, (size_t)(obj+size)));
                ((CObjectHeader*)obj)->SetFree(size);
            }
            STRESS_LOG_OOM_STACK(0);
            if (GCConfig::GetBreakOnOOM())
            {
                GCToOSInterface::DebugBreak();
            }
            return false;
        }
    }
    Object*** end_si = &SegQueueLimit (dest);
    do
    {
        //is the segment empty?
        if (!(*s_i == *(s_i-1)))
        {
            //no, swap the end elements.
            *(*s_i) = *(*(s_i-1));
        }
        //increment the fill pointer
        (*s_i)++;
        //go to the next segment.
        s_i--;
    } while (s_i > end_si);

    // We have reached the destination segment
    // store the object
    **s_i = obj;
    // increment the fill pointer
    (*s_i)++;

    LeaveFinalizeLock();

    return true;
}

相關文章