.Net Core CLR GC 的第一步过程就是标记阶段,其余的分别为,标记,计划,压缩,重定位,清扫。五个阶段。
首先来看下标记阶段,所谓的标记阶段,就是CLR 会标记存活的对象,以便于不被清扫掉,而造成程序异常。标记的第一步首先是要获取到跟对象,通过跟对象获取到正在使用的对象,然后进行一个标记,所有代码均为C++如下:
CLR的GC阶段的标记函数为:
void gc_heap::mark_phase (int condemned_gen_number, BOOL mark_only_p)
{
GCScan::GcScanRoots(GCHeap::Promote,
condemned_gen_number, max_generation,
&sc);
}
注意看 这里面GCHeap::Promote就是获取到对象然后进行一个标记的过程
void GCHeap::Promote(Object** ppObject, ScanContext* sc, uint32_t flags)
{
uint8_t* o = (uint8_t*)*ppObject;
if (o == 0)
return;
gc_heap* hp = gc_heap::heap_of (o);
dprintf (3, ("Promote %Ix", (size_t)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;
}
}
if ((o >= hp->gc_low) && (o < hp->gc_high))
{
hpt->mark_object_simple (&o THREAD_NUMBER_ARG);
}
}
而GCScanRoots函数如下:
void GCToEEInterface::GcScanRoots(promote_func* fn, int condemned, int max_gen, ScanContext* sc)
{
while ((pThread = ThreadStore::GetThreadList(pThread)) != NULL)
{
if (GCHeapUtilities::GetGCHeap()->IsThreadUsingAllocationContextHeap(
pThread->GetAllocContext(), sc->thread_number))
{
ScanStackRoots(pThread, fn, sc);
}
}
static void ScanStackRoots(Thread * pThread, promote_func* fn, ScanContext* sc)
{
pThread->StackWalkFrames( GcStackCrawlCallBack, &gcctx, flagsStackWalk);
}
可以看到 GCScanoots调用了StackWalkFrames函数,这个函数里面会循环初始化一些结构体填充,以获取到需要扫描的对象
StackWalkAction Thread::StackWalkFramesEx(
PREGDISPLAY pRD, // virtual register set at crawl start
PSTACKWALKFRAMESCALLBACK pCallback,
VOID *pData,
unsigned flags,
PTR_Frame pStartFrame
)
{
StackFrameIterator iter;
if (iter.Init(this, pStartFrame, pRD, flags) == TRUE)
{
while (iter.IsValid())
{
retVal = MakeStackwalkerCallback(&iter.m_crawl, pCallback, pData DEBUG_ARG(iter.m_uFramesProcessed));
if (retVal == SWA_ABORT)
{
break;
}
retVal = iter.Next();
if (retVal == SWA_FAILED)
{
break;
}
}
}
}
然后循环标记