| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307 |
- //-------------------------------------------------------------------------------------------------------
- // Copyright (C) Microsoft. All rights reserved.
- // Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
- //-------------------------------------------------------------------------------------------------------
- #include "CommonMemoryPch.h"
- #if defined(__clang__) && !defined(_MSC_VER)
- #include <cxxabi.h>
- #endif
- //========================================================================================================
- // HeapBlock
- //========================================================================================================
- template <typename TBlockAttributes>
- SmallNormalHeapBlockT<TBlockAttributes> *
- HeapBlock::AsNormalBlock()
- {
- Assert(IsAnyNormalBlock());
- return static_cast<SmallNormalHeapBlockT<TBlockAttributes> *>(this);
- }
- template <typename TBlockAttributes>
- SmallLeafHeapBlockT<TBlockAttributes> *
- HeapBlock::AsLeafBlock()
- {
- Assert(IsLeafBlock());
- return static_cast<SmallLeafHeapBlockT<TBlockAttributes> *>(this);
- }
- template <typename TBlockAttributes>
- SmallFinalizableHeapBlockT<TBlockAttributes> *
- HeapBlock::AsFinalizableBlock()
- {
- Assert(IsAnyFinalizableBlock());
- return static_cast<SmallFinalizableHeapBlockT<TBlockAttributes> *>(this);
- }
- #ifdef RECYCLER_VISITED_HOST
- template <typename TBlockAttributes>
- SmallRecyclerVisitedHostHeapBlockT<TBlockAttributes> *
- HeapBlock::AsRecyclerVisitedHostBlock()
- {
- Assert(IsRecyclerVisitedHostBlock());
- return static_cast<SmallRecyclerVisitedHostHeapBlockT<TBlockAttributes> *>(this);
- }
- #endif
- #ifdef RECYCLER_WRITE_BARRIER
- template <typename TBlockAttributes>
- SmallNormalWithBarrierHeapBlockT<TBlockAttributes> *
- HeapBlock::AsNormalWriteBarrierBlock()
- {
- Assert(IsNormalWriteBarrierBlock());
- return static_cast<SmallNormalWithBarrierHeapBlockT<TBlockAttributes> *>(this);
- }
- template <typename TBlockAttributes>
- SmallFinalizableWithBarrierHeapBlockT<TBlockAttributes> *
- HeapBlock::AsFinalizableWriteBarrierBlock()
- {
- Assert(IsFinalizableWriteBarrierBlock());
- return static_cast<SmallFinalizableWithBarrierHeapBlockT<TBlockAttributes> *>(this);
- }
- #endif
- void
- HeapBlock::SetNeedOOMRescan(Recycler * recycler)
- {
- Assert(!this->IsLeafBlock());
- this->needOOMRescan = true;
- recycler->SetNeedOOMRescan();
- }
- //========================================================================================================
- // SmallHeapBlock
- //========================================================================================================
- template <class TBlockAttributes>
- size_t
- SmallHeapBlockT<TBlockAttributes>::GetAllocPlusSize(uint objectCount)
- {
- // Small Heap Block Layout:
- // TrackerData * [objectCount] (Optional)
- // ObjectInfo [objectCount] (In reverse index order)
- // <Small*HeapBlock>
- size_t allocPlusSize = Math::Align<size_t>(sizeof(unsigned char) * objectCount, sizeof(size_t));
- #ifdef PROFILE_RECYCLER_ALLOC
- if (Recycler::DoProfileAllocTracker())
- {
- allocPlusSize += objectCount * sizeof(void *);
- }
- #endif
- return allocPlusSize;
- }
- template <class TBlockAttributes>
- void
- SmallHeapBlockT<TBlockAttributes>::ConstructorCommon(HeapBucket * bucket, ushort objectSize, ushort objectCount, HeapBlockType heapBlockType)
- {
- this->heapBucket = bucket;
- this->Init(objectSize, objectCount);
- Assert(heapBlockType < HeapBlock::HeapBlockType::SmallAllocBlockTypeCount + HeapBlock::HeapBlockType::MediumAllocBlockTypeCount);
- Assert(objectCount > 1 && objectCount == (this->GetPageCount() * AutoSystemInfo::PageSize) / objectSize);
- #if defined(RECYCLER_SLOW_CHECK_ENABLED)
- heapBucket->heapInfo->heapBlockCount[heapBlockType]++;
- #endif
- if (TBlockAttributes::IsSmallBlock)
- {
- Assert(heapBlockType < HeapBlockType::SmallAllocBlockTypeCount);
- }
- else
- {
- Assert(heapBlockType >= HeapBlockType::SmallAllocBlockTypeCount && heapBlockType < HeapBlockType::SmallBlockTypeCount);
- }
- DebugOnly(lastUncollectedAllocBytes = 0);
- }
- template <class TBlockAttributes>
- SmallHeapBlockT<TBlockAttributes>::SmallHeapBlockT(HeapBucket * bucket, ushort objectSize, ushort objectCount, HeapBlockType heapBlockType)
- : HeapBlock(heapBlockType),
- bucketIndex(HeapInfo::GetBucketIndex(objectSize)),
- validPointers(HeapInfo::smallAllocValidPointersMap.GetValidPointersForIndex(HeapInfo::GetBucketIndex(objectSize))),
- objectSize(objectSize), objectCount(objectCount)
- {
- ConstructorCommon(bucket, objectSize, objectCount, heapBlockType);
- }
- template <>
- SmallHeapBlockT<MediumAllocationBlockAttributes>::SmallHeapBlockT(HeapBucket * bucket, ushort objectSize, ushort objectCount, HeapBlockType heapBlockType)
- : HeapBlock((HeapBlockType)(heapBlockType)),
- bucketIndex(HeapInfo::GetMediumBucketIndex(objectSize)),
- validPointers(HeapInfo::mediumAllocValidPointersMap.GetValidPointersForIndex(HeapInfo::GetMediumBucketIndex(objectSize))),
- objectSize(objectSize), objectCount(objectCount)
- {
- ConstructorCommon(bucket, objectSize, objectCount, heapBlockType);
- }
- template <class TBlockAttributes>
- SmallHeapBlockT<TBlockAttributes>::~SmallHeapBlockT()
- {
- Assert((this->segment == nullptr && this->address == nullptr) ||
- (this->IsLeafBlock()) ||
- this->GetPageAllocator()->IsClosed());
- #if defined(RECYCLER_SLOW_CHECK_ENABLED)
- heapBucket->heapInfo->heapBlockCount[this->GetHeapBlockType()]--;
- #endif
- #if defined(RECYCLER_SLOW_CHECK_ENABLED) || ENABLE_ALLOCATIONS_DURING_CONCURRENT_SWEEP
- heapBucket->heapBlockCount--;
- #endif
- }
- template <class TBlockAttributes>
- uint
- SmallHeapBlockT<TBlockAttributes>::GetObjectBitDeltaForBucketIndex(uint bucketIndex)
- {
- return bucketIndex + 1;
- }
- template <>
- uint
- SmallHeapBlockT<MediumAllocationBlockAttributes>::GetObjectBitDeltaForBucketIndex(uint bucketIndex)
- {
- return HeapInfo::GetObjectSizeForBucketIndex<MediumAllocationBlockAttributes>(bucketIndex) / HeapConstants::ObjectGranularity;
- }
- template <class TBlockAttributes>
- uint
- SmallHeapBlockT<TBlockAttributes>::GetPageCount() const
- {
- return TBlockAttributes::PageCount;
- }
- template <>
- uint
- SmallHeapBlockT<MediumAllocationBlockAttributes>::GetUnusablePageCount()
- {
- return ((MediumAllocationBlockAttributes::PageCount * AutoSystemInfo::PageSize) % this->objectSize) / AutoSystemInfo::PageSize;
- }
- template <>
- void
- SmallHeapBlockT<MediumAllocationBlockAttributes>::ProtectUnusablePages()
- {
- size_t count = this->GetUnusablePageCount();
- if (count > 0)
- {
- char* startPage = this->address + (MediumAllocationBlockAttributes::PageCount - count) * AutoSystemInfo::PageSize;
- DWORD oldProtect;
- BOOL ret = ::VirtualProtect(startPage, count * AutoSystemInfo::PageSize, PAGE_READONLY, &oldProtect);
- Assert(ret && oldProtect == PAGE_READWRITE);
- #ifdef RECYCLER_WRITE_WATCH
- if (!CONFIG_FLAG(ForceSoftwareWriteBarrier))
- {
- ::ResetWriteWatch(startPage, count*AutoSystemInfo::PageSize);
- }
- #endif
- }
- }
- template <>
- void
- SmallHeapBlockT<MediumAllocationBlockAttributes>::RestoreUnusablePages()
- {
- size_t count = this->GetUnusablePageCount();
- if (count > 0)
- {
- char* startPage = (char*)this->address + (MediumAllocationBlockAttributes::PageCount - count) * AutoSystemInfo::PageSize;
- DWORD oldProtect;
- BOOL ret = ::VirtualProtect(startPage, count * AutoSystemInfo::PageSize, PAGE_READWRITE, &oldProtect);
- #if DBG
- HeapBlock* block = this->heapBucket->heapInfo->recycler->heapBlockMap.GetHeapBlock(this->address);
- // only need to do this after the unusable page is already successfully protected
- // currently we don't have a flag to save that, but it should not fail after it successfully added to blockmap (see SetPage() implementation)
- if (block)
- {
- Assert(block == this);
- Assert(ret && oldProtect == PAGE_READONLY);
- }
- #endif
- }
- }
- template <class TBlockAttributes>
- void
- SmallHeapBlockT<TBlockAttributes>::ClearObjectInfoList()
- {
- ushort count = this->objectCount;
- // the object info list is prefix to the object
- memset(((byte *)this) - count, 0, count);
- }
- template <class TBlockAttributes>
- byte&
- SmallHeapBlockT<TBlockAttributes>::ObjectInfo(uint index)
- {
- // See SmallHeapBlockT<TBlockAttributes>::GetAllocPlusSize for layout description
- // the object info list is prefix to the object and in reverse index order
- Assert(index < this->objectCount);
- return *(((byte *)this) - index - 1);
- }
- template <class TBlockAttributes>
- ushort
- SmallHeapBlockT<TBlockAttributes>::GetExpectedFreeObjectCount() const
- {
- Assert(this->GetRecycler()->IsSweeping());
- return objectCount - markCount;
- }
- template <class TBlockAttributes>
- uint
- SmallHeapBlockT<TBlockAttributes>::GetExpectedFreeBytes() const
- {
- return GetExpectedFreeObjectCount() * objectSize;
- }
- template <class TBlockAttributes>
- ushort
- SmallHeapBlockT<TBlockAttributes>::GetExpectedSweepObjectCount() const
- {
- return GetExpectedFreeObjectCount() - freeCount;
- }
- template <class TBlockAttributes>
- void
- SmallHeapBlockT<TBlockAttributes>::Init(ushort objectSize, ushort objectCount)
- {
- Assert(objectCount != 0);
- Assert(TBlockAttributes::IsAlignedObjectSize(objectSize));
- Assert(this->next == nullptr);
- Assert(this->freeObjectList == nullptr);
- Assert(this->freeCount == 0);
- #if ENABLE_PARTIAL_GC
- this->oldFreeCount = this->lastFreeCount = this->objectCount;
- #else
- this->lastFreeCount = this->objectCount;
- #endif
- #if ENABLE_CONCURRENT_GC
- this->isPendingConcurrentSweep = false;
- #if ENABLE_ALLOCATIONS_DURING_CONCURRENT_SWEEP
- if (CONFIG_FLAG_RELEASE(EnableConcurrentSweepAlloc))
- {
- // This flag is to identify whether this block was made available for allocations during the concurrent sweep and still needs to be swept.
- this->isPendingConcurrentSweepPrep = false;
- #if DBG || defined(RECYCLER_SLOW_CHECK_ENABLED)
- this->objectsAllocatedDuringConcurrentSweepCount = 0;
- this->hasFinishedSweepObjects = false;
- this->wasAllocatedFromDuringSweep = false;
- this->lastObjectsAllocatedDuringConcurrentSweepCount = 0;
- #endif
- }
- #endif
- #endif
- Assert(!this->isInAllocator);
- Assert(!this->isClearedFromAllocator);
- Assert(!this->isIntegratedBlock);
- }
- template <class TBlockAttributes>
- BOOL
- SmallHeapBlockT<TBlockAttributes>::ReassignPages(Recycler * recycler)
- {
- Assert(this->address == nullptr);
- Assert(this->segment == nullptr);
- PageSegment * segment;
- auto pageAllocator = this->GetPageAllocator();
- uint pagecount = this->GetPageCount();
- char * address = pageAllocator->AllocPagesPageAligned(pagecount, &segment);
- if (address == NULL)
- {
- return FALSE;
- }
- #if ENABLE_PARTIAL_GC
- recycler->autoHeap.uncollectedNewPageCount += this->GetPageCount();
- #endif
- #ifdef RECYCLER_ZERO_MEM_CHECK
- if (!this->IsLeafBlock()
- #ifdef RECYCLER_WRITE_BARRIER_ALLOC_THREAD_PAGE
- && !this->IsWithBarrier()
- #endif
- )
- {
- recycler->VerifyZeroFill(address, AutoSystemInfo::PageSize * this->GetPageCount());
- }
- #endif
- if (!this->SetPage(address, segment, recycler))
- {
- this->GetPageAllocator()->SuspendIdleDecommit();
- this->ReleasePages(recycler);
- this->GetPageAllocator()->ResumeIdleDecommit();
- return FALSE;
- }
- RECYCLER_PERF_COUNTER_ADD(FreeObjectSize, this->GetPageCount() * AutoSystemInfo::PageSize);
- RECYCLER_PERF_COUNTER_ADD(SmallHeapBlockFreeObjectSize, this->GetPageCount() * AutoSystemInfo::PageSize);
- return TRUE;
- }
- template <class TBlockAttributes>
- BOOL
- SmallHeapBlockT<TBlockAttributes>::SetPage(__in_ecount_pagesize char * baseAddress, PageSegment * pageSegment, Recycler * recycler)
- {
- char* address = baseAddress;
- Assert(HeapBlockMap32::GetLevel2Id(address) + (TBlockAttributes::PageCount - 1) < 256);
- this->segment = pageSegment;
- this->address = address;
- // Set up the page to have nothing is free
- Assert(this->freeObjectList == nullptr);
- Assert(this->IsFreeBitsValid());
- Assert(this->freeCount == 0);
- Assert(this->freeCount == this->GetFreeBitVector()->Count());
- Assert(this->objectCount == this->lastFreeCount);
- Assert(this->explicitFreeBits.Count() == 0);
- #if ENABLE_CONCURRENT_GC
- Assert(recycler->IsConcurrentMarkState() || !recycler->IsMarkState() || recycler->IsCollectionDisabled());
- #else
- Assert(!recycler->IsMarkState() || recycler->IsCollectionDisabled());
- #endif
- Assert(this->bucketIndex <= 0xFF);
- // We use the block type directly here, without the getter so that we can tell on the heap block map,
- // whether the block is a medium block or not
- if (!recycler->heapBlockMap.SetHeapBlock(this->address, this->GetPageCount() - this->GetUnusablePageCount(), this, this->heapBlockType, (byte)this->bucketIndex))
- {
- return FALSE;
- }
- // Retrieve pointer to mark bits for this block and store it locally.
- // Note, mark bits aren't guaranteed to exist until after we register with HBM.
- this->markBits = recycler->heapBlockMap.GetMarkBitVectorForPages<TBlockAttributes::BitVectorCount>(this->address);
- Assert(this->markBits);
- #if defined(_M_ARM32_OR_ARM64)
- // We need to ensure that the above writes to the SmallHeapBlock are visible to the background GC thread.
- // In particular, see Threshold 331596 -- we were seeing an old value for SmallHeapBlockT<TBlockAttributes>::markBits in ResetMarks.
- // which caused the bit vector Copy operation there to AV.
- // See also SmallHeapBlockT<TBlockAttributes>::ResetMarks.
- MemoryBarrier();
- #endif
- this->ProtectUnusablePages();
- return TRUE;
- }
- template <class TBlockAttributes>
- void
- SmallHeapBlockT<TBlockAttributes>::ReleasePages(Recycler * recycler)
- {
- Assert(recycler->collectionState != CollectionStateMark);
- Assert(segment != nullptr);
- Assert(address != nullptr);
- #if DBG
- if (this->IsLeafBlock())
- {
- RecyclerVerboseTrace(recycler->GetRecyclerFlagsTable(), _u("Releasing leaf block pages at address 0x%p\n"), address);
- }
- #endif
- char* address = this->address;
- #ifdef RECYCLER_FREE_MEM_FILL
- memset(address, DbgMemFill, AutoSystemInfo::PageSize * (this->GetPageCount()-this->GetUnusablePageCount()));
- #endif
- if (this->GetUnusablePageCount() > 0)
- {
- this->RestoreUnusablePages();
- }
- this->GetPageAllocator()->ReleasePages(address, this->GetPageCount(), this->GetPageSegment());
- this->segment = nullptr;
- this->address = nullptr;
- }
- #if ENABLE_BACKGROUND_PAGE_FREEING
- template <class TBlockAttributes>
- void
- SmallHeapBlockT<TBlockAttributes>::BackgroundReleasePagesSweep(Recycler* recycler)
- {
- recycler->heapBlockMap.ClearHeapBlock(address, this->GetPageCount() - this->GetUnusablePageCount());
- char* address = this->address;
- if (this->GetUnusablePageCount() > 0)
- {
- this->RestoreUnusablePages();
- }
- this->GetPageAllocator()->BackgroundReleasePages(address, this->GetPageCount(), this->GetPageSegment());
- this->address = nullptr;
- this->segment = nullptr;
- this->Reset();
- }
- #endif
- template <class TBlockAttributes>
- void
- SmallHeapBlockT<TBlockAttributes>::ReleasePagesShutdown(Recycler * recycler)
- {
- #if DBG
- if (this->IsLeafBlock())
- {
- RecyclerVerboseTrace(recycler->GetRecyclerFlagsTable(), _u("Releasing leaf block pages at address 0x%p\n"), address);
- }
- RemoveFromHeapBlockMap(recycler);
- // Don't release the page in shut down, the page allocator will release them faster
- // Leaf block's allocator need not be closed
- // For non-large normal heap blocks ReleasePagesShutdown could be called during shutdown cleanup when the block is still pending concurrent
- // sweep i.e. it resides in the pendingSweepList of the RecyclerSweep instance. In this case the page allocator may not have been closed yet.
- Assert(this->IsLeafBlock() || this->GetPageAllocator()->IsClosed() || this->isPendingConcurrentSweep);
- #endif
- }
- template <class TBlockAttributes>
- void
- SmallHeapBlockT<TBlockAttributes>::RemoveFromHeapBlockMap(Recycler* recycler)
- {
- recycler->heapBlockMap.ClearHeapBlock(address, this->GetPageCount() - this->GetUnusablePageCount());
- }
- template <class TBlockAttributes>
- void
- SmallHeapBlockT<TBlockAttributes>::ReleasePagesSweep(Recycler * recycler)
- {
- RemoveFromHeapBlockMap(recycler);
- ReleasePages(recycler);
- }
- template <class TBlockAttributes>
- void
- SmallHeapBlockT<TBlockAttributes>::Reset()
- {
- this->GetFreeBitVector()->ClearAll();
- this->freeCount = 0;
- this->markCount = 0;
- #if ENABLE_ALLOCATIONS_DURING_CONCURRENT_SWEEP
- if (CONFIG_FLAG_RELEASE(EnableConcurrentSweepAlloc))
- {
- #if DBG || defined(RECYCLER_SLOW_CHECK_ENABLED)
- this->hasFinishedSweepObjects = false;
- this->wasAllocatedFromDuringSweep = false;
- this->objectsMarkedDuringSweep = 0;
- this->objectsAllocatedDuringConcurrentSweepCount = 0;
- this->lastObjectsAllocatedDuringConcurrentSweepCount = 0;
- #endif
- this->isPendingConcurrentSweepPrep = false;
- }
- #endif
- #if ENABLE_PARTIAL_GC
- this->oldFreeCount = this->lastFreeCount = this->objectCount;
- #else
- this->lastFreeCount = this->objectCount;
- #endif
- this->freeObjectList = nullptr;
- this->lastFreeObjectHead = nullptr;
- this->ClearObjectInfoList();
- this->isInAllocator = false;
- #if DBG || defined(RECYCLER_STATS)
- this->GetDebugFreeBitVector()->ClearAll();
- #endif
- #if DBG
- this->isClearedFromAllocator = false;
- this->isIntegratedBlock = false;
- #endif
- // There is no page associated with this heap block,
- // and therefore we should have no mark bits either
- this->markBits = nullptr;
- Assert(this->explicitFreeBits.Count() == 0);
- }
- // Map any object address to it's object index within the heap block
- template <class TBlockAttributes>
- ushort
- SmallHeapBlockT<TBlockAttributes>::GetAddressIndex(void * objectAddress)
- {
- Assert(objectAddress >= address && objectAddress < this->GetEndAddress());
- Assert(HeapInfo::IsAlignedAddress(objectAddress));
- Assert(HeapInfo::IsAlignedAddress(address));
- unsigned int offset = (unsigned int)((char*)objectAddress - address);
- offset = offset >> HeapConstants::ObjectAllocationShift;
- ushort index = validPointers.GetAddressIndex(offset);
- Assert(index == SmallHeapBlockT<TBlockAttributes>::InvalidAddressBit ||
- index <= TBlockAttributes::MaxAddressBit);
- return index;
- }
- template <class TBlockAttributes>
- typename SmallHeapBlockT<TBlockAttributes>::SmallHeapBlockBitVector const*
- SmallHeapBlockT<TBlockAttributes>::GetInvalidBitVector()
- {
- return HeapInfo::GetInvalidBitVector<TBlockAttributes>(objectSize);
- }
- template <class TBlockAttributes>
- typename SmallHeapBlockT<TBlockAttributes>::BlockInfo const*
- SmallHeapBlockT<TBlockAttributes>::GetBlockInfo()
- {
- return HeapInfo::GetBlockInfo<TBlockAttributes>(objectSize);
- }
- template <class TBlockAttributes>
- ushort
- SmallHeapBlockT<TBlockAttributes>::GetInteriorAddressIndex(void * interiorAddress)
- {
- Assert(interiorAddress >= address && interiorAddress < this->GetEndAddress());
- Assert(HeapInfo::IsAlignedAddress(address));
- unsigned int offset = (unsigned int)((char*)interiorAddress - address);
- offset = offset >> HeapConstants::ObjectAllocationShift;
- ushort index = validPointers.GetInteriorAddressIndex(offset);
- Assert(index == SmallHeapBlockT<TBlockAttributes>::InvalidAddressBit ||
- index <= TBlockAttributes::MaxAddressBit);
- return index;
- }
- template <class TBlockAttributes>
- BOOL
- SmallHeapBlockT<TBlockAttributes>::IsInFreeObjectList(void * objectAddress)
- {
- FreeObject * freeObject = this->freeObjectList;
- while (freeObject != nullptr)
- {
- if (freeObject == objectAddress)
- {
- return true;
- }
- freeObject = freeObject->GetNext();
- }
- return false;
- }
- template <class TBlockAttributes>
- template <typename TBlockType>
- bool
- SmallHeapBlockT<TBlockAttributes>::FindHeapObjectImpl(void* objectAddress, Recycler * recycler, FindHeapObjectFlags flags, RecyclerHeapObjectInfo& heapObject)
- {
- if (flags & FindHeapObjectFlags_AllowInterior)
- {
- objectAddress = (void*) this->GetRealAddressFromInterior(objectAddress);
- if (objectAddress == nullptr)
- {
- return false;
- }
- }
- ushort index = GetAddressIndex(objectAddress);
- Assert(index != SmallHeapBlockT<TBlockAttributes>::InvalidAddressBit);
- if (index == SmallHeapBlockT<TBlockAttributes>::InvalidAddressBit)
- {
- return false;
- }
- // If we have pending object, we still need to check the free bit if the caller requested the attribute to be correct
- bool const disableCheck = ((flags & FindHeapObjectFlags_NoFreeBitVerify) != 0) ||
- ((flags & FindHeapObjectFlags_VerifyFreeBitForAttribute) != 0 && !this->HasPendingDisposeObjects());
- if (!disableCheck)
- {
- // REVIEW: Checking if an object if free is strictly not necessary
- // In all case, we should have a valid object, For memory protect heap, this is just to make sure we don't
- // free pointers that are invalid.
- #if ENABLE_CONCURRENT_GC
- if (recycler->IsConcurrentSweepExecutingState())
- {
- // TODO: unless we know the state of the heap block, we don't know.
- // skip the check for now.
- }
- else
- #endif
- {
- if (flags & FindHeapObjectFlags_ClearedAllocators)
- {
- // Heap enum has some case where it allocates, so we can't assert
- Assert(((HeapBucketT<TBlockType> *)this->heapBucket)->AllocatorsAreEmpty() || recycler->isHeapEnumInProgress);
- }
- else if (this->IsInAllocator())
- {
- ((HeapBucketT<TBlockType> *)this->heapBucket)->UpdateAllocators();
- }
- // REVIEW allocation heuristics
- if (this->EnsureFreeBitVector()->Test(this->GetObjectBitDelta() * index))
- {
- return false;
- }
- }
- }
- byte& attributes = ObjectInfo(index);
- heapObject = RecyclerHeapObjectInfo(objectAddress, recycler, this, &attributes);
- return true;
- }
- template <class TBlockAttributes>
- BOOL
- SmallHeapBlockT<TBlockAttributes>::IsValidObject(void* objectAddress)
- {
- if (objectAddress < this->GetAddress() || objectAddress >= this->GetEndAddress())
- {
- return false;
- }
- ushort index = GetAddressIndex(objectAddress);
- if (index == SmallHeapBlockT<TBlockAttributes>::InvalidAddressBit)
- {
- return false;
- }
- #if DBG
- return !this->GetDebugFreeBitVector()->Test(GetAddressBitIndex(objectAddress));
- #else
- return true;
- #endif
- }
- template <class TBlockAttributes>
- bool
- SmallHeapBlockT<TBlockAttributes>::IsInAllocator() const
- {
- return isInAllocator;
- }
- template <class TBlockAttributes>
- bool
- SmallHeapBlockT<TBlockAttributes>::HasPendingDisposeObjects()
- {
- return this->IsAnyFinalizableBlock() && this->AsFinalizableBlock<TBlockAttributes>()->HasPendingDisposeObjects();
- }
- template <class TBlockAttributes>
- bool
- SmallHeapBlockT<TBlockAttributes>::HasAnyDisposeObjects()
- {
- return this->IsAnyFinalizableBlock() && this->AsFinalizableBlock<TBlockAttributes>()->HasAnyDisposeObjects();
- }
- template <class TBlockAttributes>
- Recycler *
- SmallHeapBlockT<TBlockAttributes>::GetRecycler() const
- {
- #if DBG
- return this->heapBucket->heapInfo->recycler;
- #else
- return nullptr;
- #endif
- }
- #if DBG
- template <class TBlockAttributes>
- HeapInfo *
- SmallHeapBlockT<TBlockAttributes>::GetHeapInfo() const
- {
- return this->heapBucket->heapInfo;
- }
- template <class TBlockAttributes>
- BOOL
- SmallHeapBlockT<TBlockAttributes>::IsFreeObject(void * objectAddress)
- {
- if (objectAddress < this->GetAddress() || objectAddress >= this->GetEndAddress())
- {
- return false;
- }
- ushort index = GetAddressIndex(objectAddress);
- if (index == SmallHeapBlockT<TBlockAttributes>::InvalidAddressBit)
- {
- return false;
- }
- return this->GetDebugFreeBitVector()->Test(GetAddressBitIndex(objectAddress));
- }
- template <class TBlockAttributes>
- void
- SmallHeapBlockT<TBlockAttributes>::VerifyMarkBitVector()
- {
- this->GetRecycler()->heapBlockMap.template VerifyMarkCountForPages<TBlockAttributes::BitVectorCount>(this->address, TBlockAttributes::PageCount);
- }
- template <class TBlockAttributes>
- bool
- SmallHeapBlockT<TBlockAttributes>::IsClearedFromAllocator() const
- {
- return isClearedFromAllocator;
- }
- template <class TBlockAttributes>
- void
- SmallHeapBlockT<TBlockAttributes>::SetIsClearedFromAllocator(bool value)
- {
- isClearedFromAllocator = value;
- }
- #endif
- template <class TBlockAttributes>
- byte *
- SmallHeapBlockT<TBlockAttributes>::GetRealAddressFromInterior(void * interiorAddress)
- {
- Assert(interiorAddress >= this->address && interiorAddress < this->address + AutoSystemInfo::PageSize * this->GetPageCount());
- ushort index = GetInteriorAddressIndex(interiorAddress);
- if (index != SmallHeapBlockT<TBlockAttributes>::InvalidAddressBit)
- {
- return (byte *)this->address + index * this->GetObjectSize();
- }
- return nullptr;
- }
- template <class TBlockAttributes>
- bool
- SmallHeapBlockT<TBlockAttributes>::TestObjectMarkedBit(void* objectAddress)
- {
- Assert(this->address != nullptr);
- Assert(this->segment != nullptr);
- uint bitIndex = GetAddressBitIndex(objectAddress);
- Assert(IsValidBitIndex(bitIndex));
- return this->GetMarkedBitVector()->Test(bitIndex) != 0;
- }
- template <class TBlockAttributes>
- void
- SmallHeapBlockT<TBlockAttributes>::SetObjectMarkedBit(void* objectAddress)
- {
- Assert(this->address != nullptr);
- Assert(this->segment != nullptr);
- uint bitIndex = GetAddressBitIndex(objectAddress);
- Assert(IsValidBitIndex(bitIndex));
- this->GetMarkedBitVector()->Set(bitIndex);
- }
- #ifdef RECYCLER_MEMORY_VERIFY
- template <class TBlockAttributes>
- void
- SmallHeapBlockT<TBlockAttributes>::SetExplicitFreeBitForObject(void* objectAddress)
- {
- Assert(this->address != nullptr);
- Assert(this->segment != nullptr);
- uint bitIndex = GetAddressBitIndex(objectAddress);
- Assert(IsValidBitIndex(bitIndex));
- BOOLEAN wasSet = this->explicitFreeBits.TestAndSet(bitIndex);
- Assert(!wasSet);
- }
- template <class TBlockAttributes>
- void
- SmallHeapBlockT<TBlockAttributes>::ClearExplicitFreeBitForObject(void* objectAddress)
- {
- Assert(this->address != nullptr);
- Assert(this->segment != nullptr);
- uint bitIndex = GetAddressBitIndex(objectAddress);
- Assert(IsValidBitIndex(bitIndex));
- BOOLEAN wasSet = this->explicitFreeBits.TestAndClear(bitIndex);
- Assert(wasSet);
- }
- #endif
- #ifdef RECYCLER_VERIFY_MARK
- #if DBG
- void HeapBlock::PrintVerifyMarkFailure(Recycler* recycler, char* objectAddress, char* target)
- {
- // Due to possible GC mark optimization, the pointers may point to object
- // internal and "unaligned". Align them then FindHeapBlock.
- HeapBlock* block = recycler->FindHeapBlock(HeapInfo::GetAlignedAddress(objectAddress));
- if (block == nullptr)
- {
- return;
- }
- HeapBlock* targetBlock = recycler->FindHeapBlock(HeapInfo::GetAlignedAddress(target));
- if (targetBlock == nullptr)
- {
- return;
- }
- #ifdef TRACK_ALLOC
- Recycler::TrackerData* trackerData = nullptr;
- Recycler::TrackerData* targetTrackerData = nullptr;
- const char* typeName = nullptr;
- const char* targetTypeName = nullptr;
- uint offset = 0;
- uint targetOffset = 0;
- char* objectStartAddress = nullptr;
- char* targetStartAddress = nullptr;
- if (targetBlock->IsLargeHeapBlock())
- {
- targetOffset = (uint)(target - (char*)((LargeHeapBlock*)targetBlock)->GetRealAddressFromInterior(target));
- }
- else
- {
- targetOffset = (uint)(target - targetBlock->GetAddress()) % targetBlock->GetObjectSize(nullptr);
- }
- if (targetOffset != 0)
- {
- // "target" points to internal of an object. This is not a GC pointer.
- return;
- }
- if (Recycler::DoProfileAllocTracker())
- {
- // need CheckMemoryLeak or KeepRecyclerTrackData flag to have the tracker data and show following detailed info
- #if defined(__clang__) && !defined(_MSC_VER)
- auto getDemangledName = [](const type_info* typeinfo) ->const char*
- {
- int status;
- char buffer[1024];
- size_t buflen = 1024;
- char* name = abi::__cxa_demangle(typeinfo->name(), buffer, &buflen, &status);
- if (status != 0)
- {
- Output::Print(_u("Demangle failed: result=%d, buflen=%d\n"), status, buflen);
- }
- char* demangledName = (char*)malloc(buflen);
- memcpy(demangledName, name, buflen);
- return demangledName;
- };
- #else
- auto getDemangledName = [](const type_info* typeinfo) ->const char*
- {
- return typeinfo->name();
- };
- #endif
- if (block->IsLargeHeapBlock())
- {
- offset = (uint)(objectAddress - (char*)((LargeHeapBlock*)block)->GetRealAddressFromInterior(objectAddress));
- }
- else
- {
- offset = (uint)(objectAddress - block->address) % block->GetObjectSize(objectAddress);
- }
- objectStartAddress = objectAddress - offset;
- trackerData = (Recycler::TrackerData*)block->GetTrackerData(objectStartAddress);
- if (trackerData)
- {
- typeName = getDemangledName(trackerData->typeinfo);
- if (trackerData->isArray)
- {
- Output::Print(_u("Missing Barrier\nOn array of %S\n"), typeName);
- #ifdef STACK_BACK_TRACE
- if (CONFIG_FLAG(KeepRecyclerTrackData))
- {
- Output::Print(_u("Allocation stack:\n"));
- ((StackBackTrace*)(trackerData + 1))->Print();
- }
- #endif
- }
- else
- {
- auto dumpFalsePositive = [&]()
- {
- if (CONFIG_FLAG(Verbose))
- {
- Output::Print(_u("False Positive: %S+0x%x => 0x%p -> 0x%p\n"), typeName, offset, objectAddress, target);
- }
- };
- if (IsLikelyRuntimeFalseReference(objectStartAddress, offset, typeName))
- {
- dumpFalsePositive();
- return;
- }
- //TODO: (leish)(swb) analyze pdb to check if the field is a pointer field or not
- Output::Print(_u("Missing Barrier\nOn type %S+0x%x\n"), typeName, offset);
- }
- }
- targetStartAddress = target - targetOffset;
- targetTrackerData = (Recycler::TrackerData*)targetBlock->GetTrackerData(targetStartAddress);
- if (targetTrackerData)
- {
- targetTypeName = getDemangledName(targetTrackerData->typeinfo);
- if (targetTrackerData->isArray)
- {
- Output::Print(_u("Target type (missing barrier field type) is array item of %S\n"), targetTypeName);
- #ifdef STACK_BACK_TRACE
- if (CONFIG_FLAG(KeepRecyclerTrackData))
- {
- Output::Print(_u("Allocation stack:\n"));
- ((StackBackTrace*)(targetTrackerData + 1))->Print();
- }
- #endif
- }
- else if (targetOffset == 0)
- {
- Output::Print(_u("Target type (missing barrier field type) is %S\n"), targetTypeName);
- }
- else
- {
- Output::Print(_u("Target type (missing barrier field type) is pointing to %S+0x%x\n"), targetTypeName, targetOffset);
- }
- }
- Output::Print(_u("---------------------------------\n"));
- }
- #endif
- Output::Print(_u("Missing barrier on 0x%p, target is 0x%p\n"), objectAddress, target);
- AssertMsg(false, "Missing barrier.");
- }
- #endif // DBG
- template <class TBlockAttributes>
- void
- SmallHeapBlockT<TBlockAttributes>::VerifyMark()
- {
- Assert(!this->needOOMRescan);
- SmallHeapBlockBitVector * marked = this->GetMarkedBitVector();
- SmallHeapBlockBitVector tempFreeBits;
- this->BuildFreeBitVector(&tempFreeBits);
- SmallHeapBlockBitVector * free = &tempFreeBits;
- SmallHeapBlockBitVector const * invalid = this->GetInvalidBitVector();
- uint objectWordCount = this->GetObjectWordCount();
- Recycler * recycler = this->heapBucket->heapInfo->recycler;
- FOREACH_BITSET_IN_FIXEDBV(bitIndex, marked)
- {
- if (!free->Test(bitIndex) && !invalid->Test(bitIndex))
- {
- Assert(IsValidBitIndex(bitIndex));
- uint objectIndex = GetObjectIndexFromBitIndex((ushort)bitIndex);
- Assert((this->ObjectInfo(objectIndex) & NewTrackBit) == 0);
- // NOTE: We can't verify mark for software write barrier blocks, because they may have
- // non-pointer updates that don't trigger the write barrier, but still look like a false reference.
- // Thus, when we get here, we'll see a false reference that isn't marked.
- // Since this situation is hard to detect, just don't verify mark for write barrier blocks.
- // We could fix this if we had object layout info.
- if (!this->IsLeafBlock()
- #ifdef RECYCLER_WRITE_BARRIER
- && (!this->IsWithBarrier() || CONFIG_FLAG(ForceSoftwareWriteBarrier))
- #endif
- )
- {
- if ((ObjectInfo(objectIndex) & LeafBit) == 0)
- {
- char * objectAddress = this->address + objectIndex * objectSize;
- for (uint i = 0; i < objectWordCount; i++)
- {
- void* target = *(void**) objectAddress;
- if (recycler->VerifyMark(objectAddress, target))
- {
- #if DBG && GLOBAL_ENABLE_WRITE_BARRIER
- if (CONFIG_FLAG(ForceSoftwareWriteBarrier) && CONFIG_FLAG(VerifyBarrierBit))
- {
- this->WBVerifyBitIsSet(objectAddress);
- }
- #endif
- }
- objectAddress += sizeof(void *);
- }
- }
- }
- }
- }
- NEXT_BITSET_IN_FIXEDBV;
- }
- template <class TBlockAttributes>
- bool
- SmallHeapBlockT<TBlockAttributes>::VerifyMark(void * objectAddress, void * target)
- {
- // Because we mark through new object, we might have a false reference
- // somewhere that we have scanned before this new block is allocated
- // so the object will not be marked even though it looks like a reference
- // Can't verify when the block is new
- if (this->heapBucket->GetRecycler()->heapBlockMap.IsAddressInNewChunk(target))
- {
- return false;
- }
- ushort bitIndex = GetAddressBitIndex(target);
- bool isMarked = this->GetMarkedBitVector()->Test(bitIndex) == TRUE;
- #if DBG
- if (!isMarked)
- {
- PrintVerifyMarkFailure(this->GetRecycler(), (char*)objectAddress, (char*)target);
- }
- #else
- if (!isMarked)
- {
- DebugBreak();
- }
- #endif
- return isMarked;
- }
- #endif
- #ifdef RECYCLER_STRESS
- template <class TBlockAttributes>
- void
- SmallHeapBlockT<TBlockAttributes>::InduceFalsePositive(Recycler * recycler)
- {
- // Induce a false positive mark by marking the first object on the free list, if any.
- // Note that if the block is in the allocator, freeObjectList is not up to date.
- // So we may be marking an already-allocated block, but that's okay --
- // we call TryMark so that normal processing (including tracked object processing, etc)
- // will occur just as if we had a false reference to this object previously.
- void * falsePositive = this->freeObjectList;
- if (falsePositive != nullptr)
- {
- recycler->TryMarkNonInterior(falsePositive, nullptr);
- }
- }
- #endif
- template <class TBlockAttributes>
- void
- SmallHeapBlockT<TBlockAttributes>::ClearAllAllocBytes()
- {
- #if ENABLE_PARTIAL_GC
- this->oldFreeCount = this->lastFreeCount = this->freeCount;
- #else
- this->lastFreeCount = this->freeCount;
- #endif
- }
- #if ENABLE_ALLOCATIONS_DURING_CONCURRENT_SWEEP
- #if DBG || defined(RECYCLER_SLOW_CHECK_ENABLED)
- template <class TBlockAttributes>
- void
- SmallHeapBlockT<TBlockAttributes>::ResetConcurrentSweepAllocationCounts()
- {
- if (CONFIG_FLAG_RELEASE(EnableConcurrentSweepAlloc) && this->objectsAllocatedDuringConcurrentSweepCount > 0)
- {
- // Reset the count of objects allocated during this concurrent sweep; so we will start afresh the next time around.
- Assert(this->objectsAllocatedDuringConcurrentSweepCount == this->objectsMarkedDuringSweep);
- this->lastObjectsAllocatedDuringConcurrentSweepCount = this->objectsAllocatedDuringConcurrentSweepCount;
- this->objectsAllocatedDuringConcurrentSweepCount = 0;
- this->objectsMarkedDuringSweep = 0;
- }
- }
- #endif
- #endif
- #if ENABLE_PARTIAL_GC
- template <class TBlockAttributes>
- bool
- SmallHeapBlockT<TBlockAttributes>::DoPartialReusePage(RecyclerSweep const& recyclerSweep, uint& expectFreeByteCount)
- {
- // Partial GC page reuse heuristic
- Assert(recyclerSweep.InPartialCollectMode());
- expectFreeByteCount = GetExpectedFreeBytes();
- // PartialCollectSmallHeapBlockReuseMinFreeBytes is calculated by dwPageSize* efficacy. If efficacy is
- // high (== 1), and dwPageSize % objectSize != 0, all the pages in the bucket will be partial, and that
- // could increase in thread sweep time.
- // OTOH, if the object size is really large, the calculation below will reduce the chance for a page to be
- // partial. we might need to watch out for that.
- return (expectFreeByteCount + objectSize >= recyclerSweep.GetManager()->GetPartialCollectSmallHeapBlockReuseMinFreeBytes());
- }
- #if DBG
- // do debug assert for partial block that we are not going to sweep
- template <class TBlockAttributes>
- void
- SmallHeapBlockT<TBlockAttributes>::SweepVerifyPartialBlock(Recycler * recycler)
- {
- Assert(!this->IsLeafBlock());
- // nothing in the partialHeapBlockList is sweepable
- Assert(GetExpectedSweepObjectCount() == 0);
- }
- #endif
- template <class TBlockAttributes>
- uint
- SmallHeapBlockT<TBlockAttributes>::GetAndClearUnaccountedAllocBytes()
- {
- Assert(this->lastFreeCount >= this->freeCount);
- const ushort currentFreeCount = this->freeCount;
- uint unaccountedAllocBytes = (this->lastFreeCount - currentFreeCount) * this->objectSize;
- this->lastFreeCount = currentFreeCount;
- return unaccountedAllocBytes;
- }
- template <class TBlockAttributes>
- void
- SmallHeapBlockT<TBlockAttributes>::AdjustPartialUncollectedAllocBytes(RecyclerSweep& recyclerSweep, uint const expectSweepCount)
- {
- const uint allObjectCount = this->objectCount;
- const ushort currentFreeCount = this->freeCount;
- Assert(this->lastFreeCount == currentFreeCount);
- uint newAllocatedCount = this->oldFreeCount - currentFreeCount;
- this->oldFreeCount = currentFreeCount;
- uint newObjectExpectSweepCount = expectSweepCount;
- #if ENABLE_CONCURRENT_GC
- if (expectSweepCount != 0 && !recyclerSweep.InPartialCollect())
- {
- // We don't know which objects that we are going sweep are old and which object are new
- // So just assume one way or the other by the amount of old vs. new object in the block
- const uint allocatedObjectCount = allObjectCount - currentFreeCount;
- Assert(allocatedObjectCount >= newAllocatedCount);
- const uint oldObjectCount = allocatedObjectCount - newAllocatedCount;
- if (oldObjectCount < newAllocatedCount)
- {
- // count all of the swept object as new, but don't exceed the amount we allocated
- if (newObjectExpectSweepCount > newAllocatedCount)
- {
- newObjectExpectSweepCount = newAllocatedCount;
- }
- }
- else
- {
- // count all of the swept object as old
- newObjectExpectSweepCount = 0;
- }
- }
- #endif
- // The page can be old, or it is full (where we set lastFreeCount to 0)
- // Otherwise, the newly allocated count must be bigger then the expect sweep count
- Assert(newAllocatedCount >= newObjectExpectSweepCount);
- Assert(this->lastUncollectedAllocBytes >= newObjectExpectSweepCount * this->objectSize);
- recyclerSweep.GetManager()->SubtractSweepNewObjectAllocBytes(newObjectExpectSweepCount * this->objectSize);
- }
- #endif // RECYCLER_VERIFY_MARK
- template <class TBlockAttributes>
- uint
- SmallHeapBlockT<TBlockAttributes>::GetMarkCountForSweep()
- {
- Assert(IsFreeBitsValid());
- // Make a local copy of mark bits, so we don't modify the actual mark bits.
- SmallHeapBlockBitVector temp;
- temp.Copy(this->GetMarkedBitVector());
- // Remove any invalid bits that may have been set
- temp.Minus(this->GetInvalidBitVector());
- // Remove the mark bit for things that are still free
- if (this->freeCount != 0)
- {
- temp.Minus(this->GetFreeBitVector());
- }
- return temp.Count();
- }
- template <class TBlockAttributes>
- SweepState
- SmallHeapBlockT<TBlockAttributes>::Sweep(RecyclerSweep& recyclerSweep, bool queuePendingSweep, bool allocable, ushort finalizeCount, bool hasPendingDispose)
- {
- Assert(this->address != nullptr);
- Assert(this->segment != nullptr);
- #if ENABLE_CONCURRENT_GC
- Assert(!this->isPendingConcurrentSweep);
- #endif
- #if DBG && ENABLE_ALLOCATIONS_DURING_CONCURRENT_SWEEP
- // In concurrent sweep pass1, we mark the object directly in the mark bit vector for objects allocated during the sweep to prevent them from getting swept during the ongoing sweep itself.
- // This will make the mark bit vector on the HeapBlockMap out-of-date w.r.t. these newly allocated objects.
- if (!this->wasAllocatedFromDuringSweep)
- #endif
- {
- DebugOnly(VerifyMarkBitVector());
- }
- if (allocable)
- {
- // This block has been allocated from since the last GC.
- // We need to update its free bit vector so we can use it below.
- DebugOnly(ushort currentFreeCount = (ushort)this->GetFreeBitVector()->Count());
- Assert(freeCount == currentFreeCount);
- #if ENABLE_PARTIAL_GC
- Assert(this->lastFreeCount == 0 || this->oldFreeCount == this->lastFreeCount);
- #endif
- this->EnsureFreeBitVector();
- Assert(this->lastFreeCount >= this->freeCount);
- #if ENABLE_PARTIAL_GC
- Assert(this->oldFreeCount >= this->freeCount);
- #endif
- #if ENABLE_PARTIAL_GC
- // Accounting for partial heuristics
- recyclerSweep.GetManager()->AddUnaccountedNewObjectAllocBytes(this);
- #endif
- }
- Assert(this->freeCount == this->GetFreeBitVector()->Count());
- RECYCLER_SLOW_CHECK(CheckFreeBitVector(true));
- const uint localMarkCount = this->GetMarkCountForSweep();
- this->markCount = (ushort)localMarkCount;
- Assert(markCount <= objectCount - this->freeCount);
- const uint expectFreeCount = objectCount - localMarkCount;
- Assert(expectFreeCount >= this->freeCount);
- uint expectSweepCount = expectFreeCount - this->freeCount;
- Assert(!this->IsLeafBlock() || finalizeCount == 0);
- Recycler * recycler = recyclerSweep.GetRecycler();
- RECYCLER_STATS_INC(recycler, heapBlockCount[this->GetHeapBlockType()]);
- #if ENABLE_PARTIAL_GC
- if (recyclerSweep.GetManager()->DoAdjustPartialHeuristics() && allocable)
- {
- this->AdjustPartialUncollectedAllocBytes(recyclerSweep, expectSweepCount);
- }
- #endif
- DebugOnly(this->lastUncollectedAllocBytes = 0);
- bool noRealObjectsMarked = (localMarkCount == 0);
- #if ENABLE_ALLOCATIONS_DURING_CONCURRENT_SWEEP
- if (CONFIG_FLAG_RELEASE(EnableConcurrentSweepAlloc))
- {
- Assert(!this->IsAnyFinalizableBlock() || !this->isPendingConcurrentSweepPrep);
- // This heap block is ready to be swept concurrently.
- #if DBG || defined(RECYCLER_SLOW_CHECK_ENABLED)
- this->hasFinishedSweepObjects = false;
- #endif
- this->isPendingConcurrentSweepPrep = false;
- }
- #endif
- const bool isAllFreed = (finalizeCount == 0 && noRealObjectsMarked && !hasPendingDispose);
- if (isAllFreed)
- {
- #if ENABLE_ALLOCATIONS_DURING_CONCURRENT_SWEEP
- if (CONFIG_FLAG_RELEASE(EnableConcurrentSweepAlloc))
- {
- AssertMsg(this->objectsAllocatedDuringConcurrentSweepCount == 0, "This block shouldn't be considered EMPTY if we allocated from it during concurrent sweep.");
- }
- #endif
- recycler->NotifyFree(this);
- Assert(!this->HasPendingDisposeObjects());
- #ifdef RECYCLER_TRACE
- recycler->PrintBlockStatus(this->heapBucket, this, _u("[**26**] ending sweep Pass1, state returned SweepStateEmpty."));
- #endif
- return SweepStateEmpty;
- }
- RECYCLER_STATS_ADD(recycler, heapBlockFreeByteCount[this->GetHeapBlockType()], expectFreeCount * this->objectSize);
- Assert(!hasPendingDispose || (this->freeCount != 0));
- SweepState state = SweepStateSwept;
- if (hasPendingDispose)
- {
- state = SweepStatePendingDispose;
- }
- if (expectSweepCount == 0)
- {
- #if ENABLE_ALLOCATIONS_DURING_CONCURRENT_SWEEP
- #if DBG || defined(RECYCLER_SLOW_CHECK_ENABLED)
- if (CONFIG_FLAG_RELEASE(EnableConcurrentSweepAlloc))
- {
- this->ResetConcurrentSweepAllocationCounts();
- }
- #endif
- #endif
- // nothing has been freed
- #ifdef RECYCLER_TRACE
- if (recycler->GetRecyclerFlagsTable().Trace.IsEnabled(Js::ConcurrentSweepPhase) && CONFIG_FLAG_RELEASE(Verbose))
- {
- SweepState stateReturned = (this->freeCount == 0) ? SweepStateFull : state;
- CollectionState collectionState = recycler->collectionState;
- Output::Print(_u("[GC #%d] [HeapBucket 0x%p] HeapBlock 0x%p %s %d [CollectionState: %d] \n"), recycler->collectionCount, this->heapBucket, this, _u("[**37**] heapBlock swept. State returned:"), stateReturned, collectionState);
- }
- #endif
- return (this->freeCount == 0) ? SweepStateFull : state;
- }
- RECYCLER_STATS_INC(recycler, heapBlockSweptCount[this->GetHeapBlockType()]);
- // We need to sweep in thread if there are any finalizable object.
- // So that the PrepareFinalize() can be called before concurrent sweep
- // and other finalizer. This gives the object an opportunity before any
- // other script can be ran to clean up their references/states that are not
- // valid since we determine the object is not live any more.
- //
- // An example is the ITrackable's tracking alias. The reference to the alias
- // object needs to be clear so that the reference will not be given out again
- // in other script during concurrent sweep or finalizer called before.
- #if ENABLE_CONCURRENT_GC
- if (queuePendingSweep)
- {
- Assert(finalizeCount == 0);
- Assert(!this->HasPendingDisposeObjects());
- recyclerSweep.SetHasPendingSweepSmallHeapBlocks();
- RECYCLER_STATS_INC(recycler, heapBlockConcurrentSweptCount[this->GetHeapBlockType()]);
- // This heap block has objects that need to be swept concurrently.
- this->isPendingConcurrentSweep = true;
- #ifdef RECYCLER_TRACE
- if (recycler->GetRecyclerFlagsTable().Trace.IsEnabled(Js::ConcurrentSweepPhase))
- {
- recycler->PrintBlockStatus(this->heapBucket, this, _u("[**29**] heapBlock swept. State returned: SweepStatePendingSweep"));
- }
- #endif
- return SweepStatePendingSweep;
- }
- #else
- Assert(!recyclerSweep.IsBackground());
- #endif
- #ifdef RECYCLER_TRACE
- recycler->PrintBlockStatus(this->heapBucket, this, _u("[**16**] calling SweepObjects."));
- #endif
- SweepObjects<SweepMode_InThread>(recycler);
- if (HasPendingDisposeObjects())
- {
- Assert(finalizeCount != 0);
- #if ENABLE_ALLOCATIONS_DURING_CONCURRENT_SWEEP
- if (CONFIG_FLAG_RELEASE(EnableConcurrentSweepAlloc))
- {
- AssertMsg(this->objectsAllocatedDuringConcurrentSweepCount == 0, "Allocations during concurrent sweep not supported for finalizable blocks.");
- }
- #endif
- return SweepStatePendingDispose;
- }
- // Already swept, no more work to be done. Put it back to the queue.
- #if ENABLE_ALLOCATIONS_DURING_CONCURRENT_SWEEP
- if (CONFIG_FLAG_RELEASE(EnableConcurrentSweepAlloc) && !this->IsAnyFinalizableBlock())
- {
- #ifdef RECYCLER_TRACE
- if (recycler->GetRecyclerFlagsTable().Trace.IsEnabled(Js::ConcurrentSweepPhase) && CONFIG_FLAG_RELEASE(Verbose))
- {
- SweepState stateReturned = (this->freeCount == 0) ? SweepStateFull : state;
- CollectionState collectionState = recycler->collectionState;
- Output::Print(_u("[GC #%d] [HeapBucket 0x%p] HeapBlock 0x%p %s %d [CollectionState: %d] \n"), recycler->collectionCount, this->heapBucket, this, _u("[**38**] heapBlock swept. State returned:"), stateReturned, collectionState);
- }
- #endif
- // We always need to check the free count as we may have allocated from this block during concurrent sweep.
- return (this->freeCount == 0) ? SweepStateFull : state;
- }
- else
- #endif
- {
- return state;
- }
- }
- #if DBG
- template <class TBlockAttributes>
- uint
- SmallHeapBlockT<TBlockAttributes>::GetMarkCountOnHeapBlockMap() const
- {
- uint heapBlockMapMarkCount = 0;
- char* startPage = this->GetAddress();
- char* endPage = this->GetEndAddress();
- const HeapBlockMap& blockMap = this->GetRecycler()->heapBlockMap;
- for (char* page = startPage; page < endPage; page += AutoSystemInfo::PageSize)
- {
- heapBlockMapMarkCount += blockMap.GetPageMarkCount(page);
- }
- return heapBlockMapMarkCount;
- }
- #endif
- template <class TBlockAttributes>
- template <SweepMode mode>
- void
- SmallHeapBlockT<TBlockAttributes>::SweepObjects(Recycler * recycler)
- {
- #if ENABLE_CONCURRENT_GC
- Assert(mode == SweepMode_InThread || this->isPendingConcurrentSweep);
- Assert(mode == SweepMode_InThread || !this->IsAnyFinalizableBlock());
- #else
- Assert(mode == SweepMode_InThread);
- #endif
- Assert(this->IsFreeBitsValid());
- #if ENABLE_ALLOCATIONS_DURING_CONCURRENT_SWEEP
- AssertMsg(!hasFinishedSweepObjects, "Block in SweepObjects more than once during the ongoing sweep.");
- if (CONFIG_FLAG_RELEASE(EnableConcurrentSweepAlloc))
- {
- Assert(this->markCount != 0 || this->objectsAllocatedDuringConcurrentSweepCount > 0 || this->isForceSweeping || this->IsAnyFinalizableBlock());
- }
- else
- #endif
- {
- Assert(this->markCount != 0 || this->isForceSweeping || this->IsAnyFinalizableBlock());
- }
- Assert(this->markCount == this->GetMarkCountForSweep());
- #if DBG && ENABLE_ALLOCATIONS_DURING_CONCURRENT_SWEEP
- // In concurrent sweep pass1, we mark the object directly in the mark bit vector for objects allocated during the sweep to prevent them from getting swept during the ongoing sweep itself.
- // This will make the mark bit vector on the HeapBlockMap out-of-date w.r.t. these newly allocated objects.
- if (!this->wasAllocatedFromDuringSweep)
- #endif
- {
- DebugOnly(VerifyMarkBitVector());
- }
- SmallHeapBlockBitVector * marked = this->GetMarkedBitVector();
- DebugOnly(uint expectedSweepCount = objectCount - freeCount - markCount);
- #if DBG && ENABLE_ALLOCATIONS_DURING_CONCURRENT_SWEEP
- if (CONFIG_FLAG_RELEASE(EnableConcurrentSweepAlloc))
- {
- Assert(expectedSweepCount != 0 || this->isForceSweeping || this->objectsAllocatedDuringConcurrentSweepCount != 0);
- }
- else
- #endif
- {
- Assert(expectedSweepCount != 0 || this->isForceSweeping);
- }
- DebugOnly(uint sweepCount = 0);
- const uint localSize = objectSize;
- const uint localObjectCount = objectCount;
- const char* objectAddress = address;
- uint objectBitDelta = this->GetObjectBitDelta();
- for (uint objectIndex = 0, bitIndex = 0; objectIndex < localObjectCount; objectIndex++, bitIndex += objectBitDelta)
- {
- Assert(IsValidBitIndex(bitIndex));
- RECYCLER_STATS_ADD(recycler, objectSweepScanCount, !isForceSweeping);
- if (!marked->Test(bitIndex))
- {
- if (!this->GetFreeBitVector()->Test(bitIndex))
- {
- Assert((this->ObjectInfo(objectIndex) & ImplicitRootBit) == 0);
- FreeObject* addr = (FreeObject*)objectAddress;
- #if ENABLE_PARTIAL_GC && ENABLE_CONCURRENT_GC
- if (mode != SweepMode_ConcurrentPartial)
- #endif
- {
- // Don't call NotifyFree if we are doing a partial sweep.
- // Since we are not actually collecting the object, we will do the NotifyFree later
- // when the object is actually collected in a future Sweep.
- recycler->NotifyFree((char *)addr, this->objectSize);
- }
- #if DBG
- sweepCount++;
- #endif
- SweepObject<mode>(recycler, objectIndex, addr);
- }
- }
- #if DBG
- if (marked->Test(bitIndex))
- {
- Assert((ObjectInfo(objectIndex) & NewTrackBit) == 0);
- }
- #endif
- objectAddress += localSize;
- }
- Assert(sweepCount == expectedSweepCount);
- #if ENABLE_CONCURRENT_GC
- this->isPendingConcurrentSweep = false;
- #endif
- #if ENABLE_PARTIAL_GC && ENABLE_CONCURRENT_GC
- if (mode == SweepMode_ConcurrentPartial)
- {
- Assert(recycler->inPartialCollectMode);
- // We didn't actually collect anything, so the free bit vector should still be valid.
- Assert(IsFreeBitsValid());
- }
- else
- #endif
- {
- // Update the free bit vector
- // Need to update even if there are not swept object because finalizable object are
- // consider freed but not on the free list.
- ushort currentFreeCount = GetExpectedFreeObjectCount();
- this->GetFreeBitVector()->OrComplimented(marked);
- this->GetFreeBitVector()->Minus(this->GetInvalidBitVector());
- #if ENABLE_PARTIAL_GC
- this->oldFreeCount = this->lastFreeCount = this->freeCount = currentFreeCount;
- #else
- this->lastFreeCount = this->freeCount = currentFreeCount;
- #endif
- this->lastFreeObjectHead = this->freeObjectList;
- }
- // While allocations are allowed during concurrent sweep into still unswept blocks the
- // free bit vectors are not valid yet.
- #if ENABLE_ALLOCATIONS_DURING_CONCURRENT_SWEEP && defined(RECYCLER_SLOW_CHECK_ENABLED)
- if (CONFIG_FLAG_RELEASE(EnableConcurrentSweepAlloc) && this->objectsAllocatedDuringConcurrentSweepCount == 0)
- #endif
- {
- RECYCLER_SLOW_CHECK(CheckFreeBitVector(true));
- }
- #if ENABLE_ALLOCATIONS_DURING_CONCURRENT_SWEEP
- #if DBG || defined(RECYCLER_SLOW_CHECK_ENABLED)
- if (CONFIG_FLAG_RELEASE(EnableConcurrentSweepAlloc))
- {
- this->ResetConcurrentSweepAllocationCounts();
- }
- #endif
- #endif
- // The count of marked, non-free objects should still be the same
- Assert(this->markCount == this->GetMarkCountForSweep());
- #if ENABLE_ALLOCATIONS_DURING_CONCURRENT_SWEEP
- DebugOnly(this->hasFinishedSweepObjects = true);
- #endif
- #ifdef RECYCLER_TRACE
- recycler->PrintBlockStatus(this->heapBucket, this, _u("[**30**] finished SweepObjects, heapblock SWEPT."));
- #endif
- }
- template <class TBlockAttributes>
- void
- SmallHeapBlockT<TBlockAttributes>::EnqueueProcessedObject(FreeObject ** list, void* objectAddress, uint index)
- {
- Assert(GetAddressIndex(objectAddress) == index);
- Assert(index != SmallHeapBlockT<TBlockAttributes>::InvalidAddressBit);
- Assert(this->objectCount != 1);
- #if DBG || defined(RECYCLER_STATS)
- if (list == &this->freeObjectList)
- {
- BOOL isSet = this->GetDebugFreeBitVector()->TestAndSet(GetAddressBitIndex(objectAddress));
- Assert(!isSet);
- }
- #endif
- FillFreeMemory(objectAddress, objectSize);
- FreeObject * freeObject = (FreeObject *)objectAddress;
- freeObject->SetNext(*list);
- *list = freeObject;
- #if DBG && GLOBAL_ENABLE_WRITE_BARRIER
- if (CONFIG_FLAG(ForceSoftwareWriteBarrier) && CONFIG_FLAG(RecyclerVerifyMark))
- {
- this->WBClearObject((char*)objectAddress);
- }
- #endif
- // clear the attributes so that when we are allocating a leaf, we don't have to set the attribute
- this->ObjectInfo(index) = 0;
- }
- template <class TBlockAttributes>
- void
- SmallHeapBlockT<TBlockAttributes>::EnqueueProcessedObject(FreeObject ** list, FreeObject ** tail, void* objectAddress, uint index)
- {
- if (*tail == nullptr)
- {
- Assert(*list == nullptr);
- *tail = (FreeObject *)objectAddress;
- }
- EnqueueProcessedObject(list, objectAddress, index);
- }
- //
- // This method transfers the list of objects starting at list and ending
- // at tail to the free list.
- // In debug mode, it also makes sure that none of the objects that are
- // being prepended to the free list are already free
- //
- template <class TBlockAttributes>
- void
- SmallHeapBlockT<TBlockAttributes>::TransferProcessedObjects(FreeObject * list, FreeObject * tail)
- {
- Assert(tail != nullptr);
- Assert(list);
- #if DBG || defined(RECYCLER_STATS)
- // make sure that object we are transferred to the free list are not freed yet
- tail->SetNext(nullptr);
- FreeObject * freeObject = list;
- while (freeObject != nullptr)
- {
- Assert(!this->IsInFreeObjectList(freeObject));
- BOOL isSet = this->GetDebugFreeBitVector()->TestAndSet(GetAddressBitIndex(freeObject));
- Assert(!isSet);
- freeObject = freeObject->GetNext();
- }
- #endif
- tail->SetNext(this->freeObjectList);
- this->freeObjectList = list;
- RECYCLER_SLOW_CHECK(this->CheckDebugFreeBitVector(true));
- }
- template <class TBlockAttributes>
- uint
- SmallHeapBlockT<TBlockAttributes>::GetAndClearLastFreeCount()
- {
- uint lastFreeCount = this->lastFreeCount;
- this->lastFreeCount = 0;
- return lastFreeCount;
- }
- #ifdef RECYCLER_SLOW_CHECK_ENABLED
- template <class TBlockAttributes>
- void
- SmallHeapBlockT<TBlockAttributes>::Check(bool expectFull, bool expectPending)
- {
- #if ENABLE_ALLOCATIONS_DURING_CONCURRENT_SWEEP
- // If we allocated from this block during the concurrent sweep the free bit vectors would be invalid.
- #if DBG || defined(RECYCLER_SLOW_CHECK_ENABLED)
- if (!this->wasAllocatedFromDuringSweep)
- #endif
- #endif
- {
- if (this->IsFreeBitsValid())
- {
- CheckFreeBitVector(false);
- }
- else
- {
- CheckDebugFreeBitVector(false);
- }
- }
- Assert(expectPending == HasAnyDisposeObjects());
- // As the blocks are added to the SLIST and used from there during concurrent sweep, the expectFull assertion doesn't hold anymore.
- #if ENABLE_ALLOCATIONS_DURING_CONCURRENT_SWEEP
- if (!CONFIG_FLAG_RELEASE(EnableConcurrentSweepAlloc))
- #endif
- {
- if (this->isInAllocator || this->isClearedFromAllocator)
- {
- Assert(expectFull && !expectPending);
- }
- else
- {
- Assert(expectFull == (!this->HasFreeObject() && !HasAnyDisposeObjects()));
- }
- }
- }
- template <class TBlockAttributes>
- template <typename TBlockType>
- bool
- SmallHeapBlockT<TBlockAttributes>::GetFreeObjectListOnAllocatorImpl(FreeObject ** freeObjectList)
- {
- // not during collection, the allocator has the current info
- SmallHeapBlockAllocator<TBlockType> * head =
- &((HeapBucketT<TBlockType> *)this->heapBucket)->allocatorHead;
- SmallHeapBlockAllocator<TBlockType> * current = head;
- do
- {
- if (current->GetHeapBlock() == this)
- {
- if (current->IsFreeListAllocMode())
- {
- *freeObjectList = current->freeObjectList;
- return true;
- }
- return false;
- }
- current = current->GetNext();
- }
- while (current != head);
- return false;
- }
- template <class TBlockAttributes>
- void
- SmallHeapBlockT<TBlockAttributes>::CheckDebugFreeBitVector(bool isCollecting)
- {
- FreeObject * freeObject = this->freeObjectList;
- if (!isCollecting)
- {
- this->GetFreeObjectListOnAllocator(&freeObject);
- }
- uint verifyFreeCount = 0;
- while (freeObject != nullptr)
- {
- uint index = this->GetAddressIndex(freeObject);
- Assert(index != SmallHeapBlockT<TBlockAttributes>::InvalidAddressBit);
- Assert(this->GetDebugFreeBitVector()->Test(GetAddressBitIndex(freeObject)));
- verifyFreeCount++;
- freeObject = freeObject->GetNext();
- }
- Assert(this->GetDebugFreeBitVector()->Count() == verifyFreeCount);
- Assert(verifyFreeCount <= this->lastFreeCount);
- }
- template <class TBlockAttributes>
- void
- SmallHeapBlockT<TBlockAttributes>::CheckFreeBitVector(bool isCollecting)
- {
- // during collection, the heap block has the current info when we are verifying
- if (!isCollecting)
- {
- FreeObject * freeObjectList;
- this->GetFreeObjectListOnAllocator(&freeObjectList);
- if (freeObjectList != this->freeObjectList)
- {
- // allocator has the current info and if we have already allocated some memory,
- // the free bit vector isn't really correct, so we can't verify it.
- // Just verify the debug free bit vector
- this->CheckDebugFreeBitVector(false);
- return;
- }
- }
- SmallHeapBlockBitVector * free = this->GetFreeBitVector();
- // Shouldn't be any invalid bits set in the free bit vector
- SmallHeapBlockBitVector temp;
- temp.Copy(free);
- temp.And(this->GetInvalidBitVector());
- Assert(temp.IsAllClear());
- uint verifyFreeCount = 0;
- FreeObject * freeObject = this->freeObjectList;
- while (freeObject != nullptr)
- {
- uint bitIndex = GetAddressBitIndex(freeObject);
- Assert(IsValidBitIndex(bitIndex));
- Assert(this->GetDebugFreeBitVector()->Test(bitIndex));
- Assert(free->Test(bitIndex));
- verifyFreeCount++;
- freeObject = freeObject->GetNext();
- }
- Assert(this->GetDebugFreeBitVector()->Count() == verifyFreeCount);
- Assert(this->freeCount == this->GetFreeBitVector()->Count());
- if (this->IsAnyFinalizableBlock())
- {
- auto finalizableBlock = this->AsFinalizableBlock<TBlockAttributes>();
- // Include pending dispose objects
- finalizableBlock->ForEachPendingDisposeObject([&] (uint index) {
- uint bitIndex = ((uint)index) * this->GetObjectBitDelta();
- Assert(IsValidBitIndex(bitIndex));
- Assert(!this->GetDebugFreeBitVector()->Test(bitIndex));
- Assert(free->Test(bitIndex));
- verifyFreeCount++;
- });
- // Include disposed objects
- verifyFreeCount += finalizableBlock->CheckDisposedObjectFreeBitVector();
- }
- Assert(verifyFreeCount == this->freeCount);
- Assert(verifyFreeCount <= this->lastFreeCount);
- Assert(this->IsFreeBitsValid());
- }
- #endif
- template <class TBlockAttributes>
- typename SmallHeapBlockT<TBlockAttributes>::SmallHeapBlockBitVector *
- SmallHeapBlockT<TBlockAttributes>::EnsureFreeBitVector(bool isCollecting)
- {
- if (this->IsFreeBitsValid())
- {
- // the free object list hasn't change, so the free vector should be valid
- RECYCLER_SLOW_CHECK(CheckFreeBitVector(isCollecting));
- return this->GetFreeBitVector();
- }
- return BuildFreeBitVector();
- }
- template <class TBlockAttributes>
- typename SmallHeapBlockT<TBlockAttributes>::SmallHeapBlockBitVector *
- SmallHeapBlockT<TBlockAttributes>::BuildFreeBitVector()
- {
- SmallHeapBlockBitVector * free = this->GetFreeBitVector();
- this->freeCount = this->BuildFreeBitVector(free);
- this->lastFreeObjectHead = this->freeObjectList;
- return free;
- }
- template <class TBlockAttributes>
- ushort
- SmallHeapBlockT<TBlockAttributes>::BuildFreeBitVector(SmallHeapBlockBitVector * free)
- {
- free->ClearAll();
- ushort freeCount = 0;
- FreeObject * freeObject = this->freeObjectList;
- while (freeObject != nullptr)
- {
- uint bitIndex = GetAddressBitIndex(freeObject);
- Assert(IsValidBitIndex(bitIndex));
- Assert(this->GetDebugFreeBitVector()->Test(bitIndex));
- free->Set(bitIndex);
- freeCount++;
- freeObject = freeObject->GetNext();
- }
- Assert(this->GetDebugFreeBitVector()->Count() == freeCount);
- if (this->IsAnyFinalizableBlock())
- {
- auto finalizableBlock = this->AsFinalizableBlock<TBlockAttributes>();
- // Include pending dispose objects
- finalizableBlock->ForEachPendingDisposeObject([&] (uint index) {
- uint bitIndex = ((uint)index) * this->GetObjectBitDelta();
- Assert(IsValidBitIndex(bitIndex));
- Assert(!this->GetDebugFreeBitVector()->Test(bitIndex));
- free->Set(bitIndex);
- freeCount++;
- });
- // Include disposed objects
- freeCount += finalizableBlock->AddDisposedObjectFreeBitVector(free);
- }
- Assert(freeCount <= this->lastFreeCount);
- return freeCount;
- }
- template <class TBlockAttributes>
- void
- SmallHeapBlockT<TBlockAttributes>::MarkImplicitRoots()
- {
- uint localObjectCount = this->GetObjectCount();
- uint localObjectBitDelta = this->GetObjectBitDelta();
- uint localMarkCount = 0;
- SmallHeapBlockBitVector * mark = this->GetMarkedBitVector();
- #if DBG
- uint localObjectSize = this->GetObjectSize();
- Assert(localObjectSize <= HeapConstants::MaxMediumObjectSize);
- ushort markCountPerPage[TBlockAttributes::PageCount];
- for (uint i = 0; i < TBlockAttributes::PageCount; i++)
- {
- markCountPerPage[i] = 0;
- }
- #endif
- for (uint i = 0; i < localObjectCount; i++)
- {
- // REVIEW: This may include free object. It is okay to mark them and scan them
- // But kind inefficient.
- if (this->ObjectInfo(i) & ImplicitRootBit)
- {
- #if DBG
- {
- int index = (i * localObjectSize) / AutoSystemInfo::PageSize;
- Assert(index < TBlockAttributes::PageCount);
- markCountPerPage[index]++;
- }
- #endif
- mark->Set(localObjectBitDelta * i);
- localMarkCount++;
- }
- }
- Assert(mark->Count() == localMarkCount);
- this->markCount = (ushort)localMarkCount;
- #if DBG
- HeapBlockMap& map = this->GetRecycler()->heapBlockMap;
- for (uint i = 0; i < TBlockAttributes::PageCount; i++)
- {
- map.SetPageMarkCount(this->address + (i * AutoSystemInfo::PageSize), markCountPerPage[i]);
- }
- #endif
- }
- template <class TBlockAttributes>
- void
- SmallHeapBlockT<TBlockAttributes>::EnumerateObjects(ObjectInfoBits infoBits, void (*CallBackFunction)(void * address, size_t size))
- {
- ForEachAllocatedObject(infoBits, [=](uint index, void * objectAddress)
- {
- CallBackFunction(objectAddress, this->objectSize);
- });
- }
- template <class TBlockAttributes>
- inline
- void SmallHeapBlockT<TBlockAttributes>::FillFreeMemory(__in_bcount(size) void * address, size_t size)
- {
- #ifdef RECYCLER_MEMORY_VERIFY
- if (this->heapBucket->heapInfo->recycler->VerifyEnabled())
- {
- memset(address, Recycler::VerifyMemFill, size);
- return;
- }
- #endif
- if (this->IsLeafBlock()
- #ifdef RECYCLER_WRITE_BARRIER_ALLOC_THREAD_PAGE
- || this->IsWithBarrier()
- #endif
- )
- {
- return;
- }
- // REVIEW: Do DbgMemFill on debug build?
- #if defined(_M_IX86)
- uint qwordCount = size / sizeof(uint64) ;
- switch (qwordCount)
- {
- case 2:
- ((uint64*)address)[0] = 0;
- ((uint64*)address)[1] = 0;
- break;
- case 4:
- ((uint64*)address)[0] = 0;
- ((uint64*)address)[1] = 0;
- ((uint64*)address)[2] = 0;
- ((uint64*)address)[3] = 0;
- break;
- case 6:
- ((uint64*)address)[0] = 0;
- ((uint64*)address)[1] = 0;
- ((uint64*)address)[2] = 0;
- ((uint64*)address)[3] = 0;
- ((uint64*)address)[4] = 0;
- ((uint64*)address)[5] = 0;
- break;
- default:
- memset(address, 0, size);
- }
- #else
- memset(address, 0, size);
- #endif
- }
- #ifdef RECYCLER_MEMORY_VERIFY
- template <class TBlockAttributes>
- void SmallHeapBlockT<TBlockAttributes>::VerifyBumpAllocated(_In_ char * bumpAllocateAddress)
- {
- ushort verifyFinalizeCount = 0;
- Recycler * recycler = this->heapBucket->heapInfo->recycler;
- char * memBlock = this->GetAddress();
- for (uint i = 0; i < objectCount; i++)
- {
- if (memBlock >= bumpAllocateAddress)
- {
- Recycler::VerifyCheckFill(memBlock + sizeof(FreeObject), this->GetObjectSize() - sizeof(FreeObject));
- }
- else
- {
- recycler->VerifyCheckPad(memBlock, this->GetObjectSize());
- if ((this->ObjectInfo(i) & FinalizeBit) != 0)
- {
- if (this->IsAnyFinalizableBlock())
- {
- verifyFinalizeCount++;
- }
- else
- {
- Recycler::VerifyCheck(false, _u("Non-Finalizable block should not have finalizable objects"),
- this->GetAddress(), &this->ObjectInfo(i));
- }
- }
- }
- memBlock += this->GetObjectSize();
- }
- }
- template <class TBlockAttributes>
- void SmallHeapBlockT<TBlockAttributes>::Verify(bool pendingDispose)
- {
- ushort verifyFinalizeCount = 0;
- SmallHeapBlockBitVector tempFree;
- SmallHeapBlockBitVector *free = &tempFree;
- SmallHeapBlockBitVector tempPending;
- this->BuildFreeBitVector(free);
- Recycler * recycler = this->heapBucket->heapInfo->recycler;
- char * memBlock = this->GetAddress();
- uint objectBitDelta = this->GetObjectBitDelta();
- Recycler::VerifyCheck(!pendingDispose || this->IsAnyFinalizableBlock(),
- _u("Non-finalizable block shouldn't be disposing. May have corrupted block type."),
- this->GetAddress(), (void *)&this->heapBlockType);
- if (HasPendingDisposeObjects())
- {
- Assert(pendingDispose);
- // Pending object are not free yet, they don't have memory cleared.
- this->AsFinalizableBlock<TBlockAttributes>()->ForEachPendingDisposeObject([&](uint index) {
- uint bitIndex = ((uint)index) * this->GetObjectBitDelta();
- Assert(IsValidBitIndex(bitIndex));
- Assert(!this->GetDebugFreeBitVector()->Test(bitIndex));
- Assert(free->Test(bitIndex));
- tempPending.Set(bitIndex);
- // We are a pending dispose block, so the finalize count hasn't been update yet.
- // Including the pending objects in the finalize count
- verifyFinalizeCount++;
- });
- }
- for (uint i = 0; i < objectCount; i++)
- {
- if (free->Test(i * objectBitDelta))
- {
- if (!tempPending.Test(i * objectBitDelta))
- {
- char * nextFree = (char *)((FreeObject *)memBlock)->GetNext();
- Recycler::VerifyCheck(nextFree == nullptr
- || (nextFree >= address && nextFree < this->GetEndAddress()
- && free->Test(GetAddressBitIndex(nextFree))),
- _u("SmallHeapBlock memory written to after freed"), memBlock, memBlock);
- Recycler::VerifyCheckFill(memBlock + sizeof(FreeObject), this->GetObjectSize() - sizeof(FreeObject));
- }
- }
- else
- {
- if (explicitFreeBits.Test(i * objectBitDelta))
- {
- char * nextFree = (char *)((FreeObject *)memBlock)->GetNext();
- HeapBlock* nextFreeHeapBlock = this;
- if (nextFree != nullptr)
- {
- nextFreeHeapBlock = recycler->FindHeapBlock(nextFree);
- }
- Recycler::VerifyCheck(nextFree == nullptr
- || (nextFree >= address && nextFree < this->GetEndAddress()
- && explicitFreeBits.Test(GetAddressBitIndex(nextFree)))
- || nextFreeHeapBlock->GetObjectSize(nextFree) == this->objectSize,
- _u("SmallHeapBlock memory written to after freed"), memBlock, memBlock);
- recycler->VerifyCheckPadExplicitFreeList(memBlock, this->GetObjectSize());
- }
- else
- {
- recycler->VerifyCheckPad(memBlock, this->GetObjectSize());
- }
- if ((this->ObjectInfo(i) & FinalizeBit) != 0)
- {
- if (this->IsAnyFinalizableBlock())
- {
- verifyFinalizeCount++;
- }
- else
- {
- Recycler::VerifyCheck(false, _u("Non-Finalizable block should not have finalizable objects"),
- this->GetAddress(), &this->ObjectInfo(i));
- }
- }
- }
- memBlock += this->GetObjectSize();
- }
- if (this->IsAnyFinalizableBlock())
- {
- Recycler::VerifyCheck(this->AsFinalizableBlock<TBlockAttributes>()->finalizeCount == verifyFinalizeCount,
- _u("SmallHeapBlock finalize count mismatch"), this->GetAddress(), &this->AsFinalizableBlock<TBlockAttributes>()->finalizeCount);
- }
- else
- {
- Assert(verifyFinalizeCount == 0);
- }
- }
- #endif
- #if ENABLE_MEM_STATS
- template <class TBlockAttributes>
- void
- SmallHeapBlockT<TBlockAttributes>::AggregateBlockStats(HeapBucketStats& stats, bool isAllocatorBlock, FreeObject* freeObjectList, bool isBumpAllocated)
- {
- if (this->segment == nullptr || this->IsInAllocator() != isAllocatorBlock)
- {
- return; // skip empty blocks, or blocks mismatching isInAllocator to avoid double count
- }
- DUMP_FRAGMENTATION_STATS_ONLY(stats.totalBlockCount++);
- ushort blockObjectCount = this->objectCount;
- BVIndex blockFreeCount = this->GetFreeBitVector()->Count();
- ushort blockObjectSize = this->objectSize;
- uint objectCount = 0;
- if (isBumpAllocated)
- {
- objectCount = static_cast<uint>(((char*) freeObjectList - this->address) / blockObjectSize);
- }
- else
- {
- objectCount = blockObjectCount;
- // If this is an allocator block, remove the free objects on the allocator
- // from this count. Otherwise, remove the free objects found in the free bit vector
- if (freeObjectList)
- {
- Assert(isAllocatorBlock);
- FreeObject* next = freeObjectList->GetNext();
- while (next != nullptr && next != freeObjectList)
- {
- objectCount--;
- next = next->GetNext();
- }
- }
- else
- {
- objectCount -= blockFreeCount;
- }
- }
- DUMP_FRAGMENTATION_STATS_ONLY(stats.objectCount += objectCount);
- stats.objectByteCount += (objectCount * blockObjectSize);
- stats.totalByteCount += this->GetPageCount() * AutoSystemInfo::PageSize;
- #ifdef DUMP_FRAGMENTATION_STATS
- if (!isAllocatorBlock)
- {
- if (this->IsAnyFinalizableBlock())
- {
- auto finalizableBlock = this->AsFinalizableBlock<TBlockAttributes>();
- stats.finalizeCount += (finalizableBlock->GetFinalizeCount());
- }
- }
- #endif
- }
- #endif
- #ifdef RECYCLER_PERF_COUNTERS
- template <class TBlockAttributes>
- void
- SmallHeapBlockT<TBlockAttributes>::UpdatePerfCountersOnFree()
- {
- Assert(markCount == 0);
- Assert(this->IsFreeBitsValid());
- size_t usedCount = (objectCount - freeCount);
- size_t usedBytes = usedCount * objectSize;
- RECYCLER_PERF_COUNTER_SUB(SmallHeapBlockLiveObject, usedCount);
- RECYCLER_PERF_COUNTER_SUB(SmallHeapBlockLiveObjectSize, usedBytes);
- RECYCLER_PERF_COUNTER_SUB(SmallHeapBlockFreeObjectSize, this->GetPageCount() * AutoSystemInfo::PageSize - usedBytes);
- RECYCLER_PERF_COUNTER_SUB(LiveObject, usedCount);
- RECYCLER_PERF_COUNTER_SUB(LiveObjectSize, usedBytes);
- RECYCLER_PERF_COUNTER_SUB(FreeObjectSize, this->GetPageCount() * AutoSystemInfo::PageSize - usedBytes);
- }
- #endif
- #ifdef PROFILE_RECYCLER_ALLOC
- template <class TBlockAttributes>
- void *
- SmallHeapBlockT<TBlockAttributes>::GetTrackerData(void * address)
- {
- Assert(Recycler::DoProfileAllocTracker());
- ushort index = this->GetAddressIndex(address);
- Assert(index != SmallHeapBlockT<TBlockAttributes>::InvalidAddressBit);
- return this->GetTrackerDataArray()[index];
- }
- template <class TBlockAttributes>
- void
- SmallHeapBlockT<TBlockAttributes>::SetTrackerData(void * address, void * data)
- {
- Assert(Recycler::DoProfileAllocTracker());
- ushort index = this->GetAddressIndex(address);
- Assert(index != SmallHeapBlockT<TBlockAttributes>::InvalidAddressBit);
- void* existingTrackerData = this->GetTrackerDataArray()[index];
- Assert((existingTrackerData == nullptr || data == nullptr) ||
- (existingTrackerData == &Recycler::TrackerData::ExplicitFreeListObjectData || data == &Recycler::TrackerData::ExplicitFreeListObjectData));
- this->GetTrackerDataArray()[index] = data;
- }
- template <class TBlockAttributes>
- void **
- SmallHeapBlockT<TBlockAttributes>::GetTrackerDataArray()
- {
- // See SmallHeapBlockT<TBlockAttributes>::GetAllocPlusSize for layout description
- return (void **)((char *)this - SmallHeapBlockT<TBlockAttributes>::GetAllocPlusSize(this->objectCount));
- }
- #endif
- #ifdef RECYCLER_WRITE_BARRIER
- template <class TBlockAttributes>
- bool
- SmallHeapBlockT<TBlockAttributes>::IsWithBarrier() const
- {
- return IsNormalWriteBarrierBlock() || IsFinalizableWriteBarrierBlock();
- }
- #endif
- namespace Memory
- {
- // Instantiate the template
- template class SmallHeapBlockT<SmallAllocationBlockAttributes>;
- template class SmallHeapBlockT<MediumAllocationBlockAttributes>;
- };
- #define TBlockTypeAttributes SmallAllocationBlockAttributes
- #include "SmallBlockDeclarations.inl"
- #undef TBlockTypeAttributes
- #define TBlockTypeAttributes MediumAllocationBlockAttributes
- #include "SmallBlockDeclarations.inl"
- #undef TBlockTypeAttributes
|