首先是从mark_phase标记阶段开始的(gc.cpp->19472,coreclr 2.2.5)
void gc_heap::mark_phase (int condemned_gen_number, BOOL mark_only_p)
{
GCScan::GcScanRoots(GCHeap::Promote,
condemned_gen_number, max_generation,
&sc);
}
mark_phase里面调用了gcscanroots函数,并且传递了一个函数回调函数,promote,这个其实是过滤获取到的Object,对其进行标记。
void GCToEEInterface::GcScanRoots(promote_func* fn, int condemned, int max_gen, ScanContext* sc)
{
while ((pThread = ThreadStore::GetThreadList(pThread)) != NULL)
{
STRESS_LOG2(LF_GC | LF_GCROOTS, LL_INFO100, "{ Starting scan of Thread %p ID = %x\n", pThread, pThread->GetThreadId());
if (GCHeapUtilities::GetGCHeap()->IsThreadUsingAllocationContextHeap(
pThread->GetAllocContext(), sc->thread_number))
{
sc->thread_under_crawl = pThread;
#ifdef FEATURE_EVENT_TRACE
sc->dwEtwRootKind = kEtwGCRootKindStack;
#endif // FEATURE_EVENT_TRACE
ScanStackRoots(pThread, fn, sc);
#ifdef FEATURE_EVENT_TRACE
sc->dwEtwRootKind = kEtwGCRootKindOther;
#endif // FEATURE_EVENT_TRACE
}
STRESS_LOG2(LF_GC | LF_GCROOTS, LL_INFO100, "Ending scan of Thread %p ID = 0x%x }\n", pThread, pThread->GetThreadId());
}
}
gcscanroots里面遍历循环所有线程thread,然后调用扫描栈函数scanstackroots。
static void ScanStackRoots(Thread * pThread, promote_func* fn, ScanContext* sc)
{
pThread->StackWalkFrames( GcStackCrawlCallBack, &gcctx, flagsStackWalk);
}
这里面调用了stackwalkframes函数
StackWalkAction Thread::StackWalkFrames(PSTACKWALKFRAMESCALLBACK pCallback,
VOID *pData,
unsigned flags,
PTR_Frame pStartFrame)
{
return StackWalkFramesEx(&rd, pCallback, pData, flags, pStartFrame);
}
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;
}
}
}
}
这个stackwalkframesex里面会遍历传进来的线程的栈帧,然后进行处理。首先要做的是实例化stackframeiterator,然后遍历循环,利用MakeStackwalkerCallback来处理,用iter.next循环到当前线程的下一帧。
StackWalkAction Thread::MakeStackwalkerCallback(
CrawlFrame* pCF,
PSTACKWALKFRAMESCALLBACK pCallback,
VOID* pData
DEBUG_ARG(UINT32 uFramesProcessed))
{
StackWalkAction swa = pCallback(pCF, (VOID*)pData);
return swa;
}
这里面调用了毁掉函数pcallback,传递了两个参数,CrawlFrame帧栈和GCCONTEXT数据
StackWalkAction GcStackCrawlCallBack(CrawlFrame* pCF, VOID* pData)
{
ICodeManager * pCM = pCF->GetCodeManager();
pCM->EnumGcRefs(pCF->GetRegisterSet(),
pCF->GetCodeInfo(),
flags,
GcEnumObject,
pData,
relOffsetOverride);
}
继续看EnumGcRefs,这个函数看名字就知道是便利托管堆帧栈
bool EECodeManager::EnumGcRefs( PREGDISPLAY pRD,
EECodeInfo *pCodeInfo,
unsigned flags,
GCEnumCallback pCallBack,
LPVOID hCallBack,
DWORD relOffsetOverride)
{
if (!gcInfoDecoder.EnumerateLiveSlots(
pRD,
reportScratchSlots,
flags,
pCallBack,
hCallBack
))
{
return false;
}
}
bool GcInfoDecoder::EnumerateLiveSlots(
PREGDISPLAY pRD,
bool reportScratchSlots,
unsigned inputFlags,
GCEnumCallback pCallBack,
void * hCallBack
)
{
GcSlotDecoder slotDecoder;
slotDecoder.DecodeSlotTable(m_Reader);
ReportSlotToGC(slotDecoder,
slotIndex,
pRD,
reportScratchSlots,
inputFlags,
pCallBack,
hCallBack
);
}
inline void ReportSlotToGC(
GcSlotDecoder& slotDecoder,
UINT32 slotIndex,
PREGDISPLAY pRD,
bool reportScratchSlots,
unsigned inputFlags,
GCEnumCallback pCallBack,
void * hCallBack
)
{ ReportStackSlotToGC(
spOffset,
spBase,
pSlot->Flags,
pRD,
inputFlags,
pCallBack,
hCallBack
);
}
不解释,他直接是调用,我们进去看看
void GcInfoDecoder::ReportStackSlotToGC(
INT32 spOffset,
GcStackSlotBase spBase,
unsigned gcFlags,
PREGDISPLAY pRD,
unsigned flags,
GCEnumCallback pCallBack,
void * hCallBack)
{
GCINFODECODER_CONTRACT;
OBJECTREF* pObjRef = GetStackSlot(spOffset, spBase, pRD);
_ASSERTE( IS_ALIGNED( pObjRef, sizeof( Object* ) ) );
#ifdef _DEBUG
LOG((LF_GCROOTS, LL_INFO1000, /* Part One */
"Reporting %s" FMT_STK,
( (GC_SP_REL == spBase) ? "" :
((GC_CALLER_SP_REL == spBase) ? "caller's " :
((GC_FRAMEREG_REL == spBase) ? "frame " : "<unrecognized GcStackSlotBase> "))),
DBG_STK(spOffset) ));
LOG((LF_GCROOTS, LL_INFO1000, /* Part Two */
"at" FMT_ADDR "as ", DBG_ADDR(pObjRef) ));
VALIDATE_ROOT((gcFlags & GC_CALL_INTERIOR), hCallBack, pObjRef);
LOG_PIPTR(pObjRef, gcFlags, hCallBack);
#endif
gcFlags |= CHECK_APP_DOMAIN;
pCallBack(hCallBack, pObjRef, gcFlags DAC_ARG(DacSlotLocation(GetStackReg(spBase), spOffset, true)));
}
ReportStackSlotToGC函数,通过寄存rbp+XX 指向的地址,获取到当前Object的,引用类型地址,然后把传给标记函数Promote,让其进行对象存活标记。
至于这个对象是怎么获取到,它是通过GCToken的gcinfo 信息位获取的,里面包含了对象相对于rbp寄存器的偏移地址。
gcroot,看似很神秘,其实东西就这么多。如果你有任何疑问可以加入QQ群:
676817308一起探讨学习。也可以扫码关注我,里面有更多技术分享