HeapBlock.h 35 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984
  1. //-------------------------------------------------------------------------------------------------------
  2. // Copyright (C) Microsoft. All rights reserved.
  3. // Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
  4. //-------------------------------------------------------------------------------------------------------
  5. #pragma once
  6. #include "HeapBucketStats.h"
  7. class ScriptMemoryDumper;
  8. #include "HeapBucketStats.h"
  9. namespace Memory
  10. {
  11. #ifdef RECYCLER_PAGE_HEAP
  12. enum class PageHeapBlockTypeFilter;
  13. #ifdef ENABLE_DEBUG_CONFIG_OPTIONS
  14. #define PageHeapVerboseTrace(flags, ...) \
  15. if (flags.Verbose && flags.Trace.IsEnabled(Js::PageHeapPhase)) \
  16. { \
  17. Output::Print(__VA_ARGS__); \
  18. }
  19. #define PageHeapTrace(flags, ...) \
  20. if (flags.Trace.IsEnabled(Js::PageHeapPhase)) \
  21. { \
  22. Output::Print(__VA_ARGS__); \
  23. }
  24. #else
  25. #define PageHeapVerboseTrace(...)
  26. #define PageHeapTrace(...)
  27. #endif
  28. #else
  29. #define PageHeapVerboseTrace(...)
  30. #define PageHeapTrace(...)
  31. #endif
  32. class Recycler;
  33. class HeapBucket;
  34. class HeapInfo;
  35. template <typename TBlockType> class HeapBucketT;
  36. class RecyclerSweep;
  37. class MarkContext;
  38. #if ENABLE_MEM_STATS
  39. #ifdef DUMP_FRAGMENTATION_STATS
  40. #define DUMP_FRAGMENTATION_STATS_ONLY(x) x
  41. #define DUMP_FRAGMENTATION_STATS_IS(x) x
  42. #else
  43. #define DUMP_FRAGMENTATION_STATS_ONLY(x)
  44. #define DUMP_FRAGMENTATION_STATS_IS(x) false
  45. #endif
  46. #endif // ENABLE_MEM_STATS
  47. #if defined(PROFILE_RECYCLER_ALLOC) || defined(RECYCLER_MEMORY_VERIFY) || defined(MEMSPECT_TRACKING) || defined(RECYCLER_PERF_COUNTERS) || defined(ETW_MEMORY_TRACKING)
  48. #define RECYCLER_TRACK_NATIVE_ALLOCATED_OBJECTS
  49. #endif
  50. #ifdef RECYCLER_SLOW_CHECK_ENABLED
  51. #define RECYCLER_SLOW_CHECK(x) x
  52. #define RECYCLER_SLOW_CHECK_IF(cond, x) if (cond) { x; }
  53. #else
  54. #define RECYCLER_SLOW_CHECK(x)
  55. #define RECYCLER_SLOW_CHECK_IF(cond, x)
  56. #endif
  57. // ObjectInfoBits is unsigned short, but only the lower byte is stored as the object attribute
  58. // The upper bits are used to pass other information about allocation (e.g. NoDisposeBit)
  59. //
  60. enum ObjectInfoBits : unsigned short
  61. {
  62. // Bits that are actually stored in ObjectInfo
  63. NoBit = 0x00, // assume an allocation is not leaf unless LeafBit is specified.
  64. FinalizeBit = 0x80, // Indicates that the object has a finalizer
  65. PendingDisposeBit = 0x40, // Indicates that the object is pending dispose
  66. LeafBit = 0x20, // Indicates that the object is a leaf-object (objects without this bit need to be scanned)
  67. TrackBit = 0x10, // Indicates that the object is a TrackableObject, but has also been overloaded to mean traced for RecyclerVisitedHostHeap objects
  68. ImplicitRootBit = 0x08,
  69. NewTrackBit = 0x04, // Tracked object is newly allocated and hasn't been process by concurrent GC
  70. MemoryProfilerOldObjectBit = 0x02,
  71. EnumClass_1_Bit = 0x01, // This can be extended to add more enumerable classes (if we still have bits left)
  72. // Mask for above bits
  73. StoredObjectInfoBitMask = 0xFF,
  74. // Bits that implied by the block type, and thus don't need to be stored (for small blocks)
  75. // Note, LeafBit is used in finalizable blocks, thus is not always implied by the block type
  76. // GC-TODO: FinalizeBit doesn't need to be stored since we have separate bucket for them.
  77. // We can move it the upper byte.
  78. #ifdef RECYCLER_WRITE_BARRIER
  79. WithBarrierBit = 0x0100,
  80. #endif
  81. #ifdef RECYCLER_VISITED_HOST
  82. RecyclerVisitedHostBit = 0x0200,
  83. #endif
  84. // Mask for above bits
  85. InternalObjectInfoBitMask = 0x03FF,
  86. // Bits that only affect allocation behavior, not mark/sweep/etc
  87. ClientTrackedBit = 0x0400, // This allocation is client tracked
  88. TraceBit = 0x0800,
  89. // Additional definitions based on above
  90. #ifdef RECYCLER_STATS
  91. NewFinalizeBit = NewTrackBit, // Use to detect if the background thread has counted the finalizable object in stats
  92. #else
  93. NewFinalizeBit = 0x00,
  94. #endif
  95. #ifdef RECYCLER_WRITE_BARRIER
  96. FinalizableWithBarrierBit = WithBarrierBit | FinalizeBit,
  97. #endif
  98. // Allocation bits
  99. FinalizableLeafBits = NewFinalizeBit | FinalizeBit | LeafBit,
  100. FinalizableObjectBits = NewFinalizeBit | FinalizeBit,
  101. #ifdef RECYCLER_WRITE_BARRIER
  102. FinalizableWithBarrierObjectBits = NewFinalizeBit | FinalizableWithBarrierBit,
  103. #endif
  104. ClientFinalizableObjectBits = NewFinalizeBit | ClientTrackedBit | FinalizeBit,
  105. ClientTrackableLeafBits = NewTrackBit | ClientTrackedBit | TrackBit | FinalizeBit | LeafBit,
  106. ClientTrackableObjectBits = NewTrackBit | ClientTrackedBit | TrackBit | FinalizeBit,
  107. #ifdef RECYCLER_WRITE_BARRIER
  108. ClientTrackableObjectWithBarrierBits = ClientTrackableObjectBits | WithBarrierBit,
  109. ClientFinalizableObjectWithBarrierBits = ClientFinalizableObjectBits | WithBarrierBit,
  110. #endif
  111. WeakReferenceEntryBits = LeafBit,
  112. ImplicitRootLeafBits = LeafBit | ImplicitRootBit,
  113. // Pending dispose objects should have LeafBit set and no others
  114. PendingDisposeObjectBits = PendingDisposeBit | LeafBit,
  115. #ifdef RECYCLER_VISITED_HOST
  116. // Bits for use with recycler visited host heap block.
  117. // Recycler visited host heap block will both mark and finalize based on IRecyclerVisitedHost v-table, as specified
  118. // by TrackBit and FinalizeBit. These objects are expected to be allocated by chakra, but implemented by
  119. // the host, including construction of the IRecyclerVisitedHost v-table.
  120. //
  121. // RecyclerVisitedHostBit is implicit in the heap block type and thus isn't part of the StoredObjectInfoBitMask.
  122. // LeafBit is also set for any object that is not precisely traced.
  123. RecyclerVisitedHostTracedBits = RecyclerVisitedHostBit | TrackBit | NewTrackBit,
  124. RecyclerVisitedHostFinalizableBits = RecyclerVisitedHostBit | LeafBit | FinalizeBit | NewFinalizeBit,
  125. RecyclerVisitedHostTracedFinalizableBits = RecyclerVisitedHostTracedBits | FinalizeBit,
  126. // These set of bits describe the four possible types of blocktype bits for recycler visited host heap blocks.
  127. // These are the four combinations of the above bits, AND'd with GetBlockTypeBitMask.
  128. // In the end, these are treated the same in terms of which heap block/bucket type they end up using and
  129. // but are defined here for ease of use.
  130. RecyclerVisitedHostFinalizableBlockTypeBits = RecyclerVisitedHostBit | LeafBit | FinalizeBit,
  131. RecyclerVisitedHostTracedFinalizableBlockTypeBits = RecyclerVisitedHostBit | FinalizeBit,
  132. #endif
  133. GetBlockTypeBitMask = FinalizeBit | LeafBit
  134. #ifdef RECYCLER_WRITE_BARRIER
  135. | WithBarrierBit
  136. #endif
  137. #ifdef RECYCLER_VISITED_HOST
  138. | RecyclerVisitedHostBit
  139. #endif
  140. ,
  141. CollectionBitMask = LeafBit | FinalizeBit | TrackBit | NewTrackBit, // Bits relevant to collection
  142. EnumClassMask = EnumClass_1_Bit,
  143. };
  144. enum ResetMarkFlags
  145. {
  146. ResetMarkFlags_None = 0x0,
  147. ResetMarkFlags_Background = 0x1,
  148. ResetMarkFlags_ScanImplicitRoot = 0x2,
  149. // For in thread GC
  150. ResetMarkFlags_InThread = ResetMarkFlags_None,
  151. ResetMarkFlags_InThreadImplicitRoots = ResetMarkFlags_None | ResetMarkFlags_ScanImplicitRoot,
  152. // For background GC
  153. ResetMarkFlags_InBackgroundThread = ResetMarkFlags_Background,
  154. ResetMarkFlags_InBackgroundThreadImplicitRoots = ResetMarkFlags_Background | ResetMarkFlags_ScanImplicitRoot,
  155. // For blocking synchronized GC
  156. ResetMarkFlags_Synchronized = ResetMarkFlags_None,
  157. ResetMarkFlags_SynchronizedImplicitRoots = ResetMarkFlags_None | ResetMarkFlags_ScanImplicitRoot,
  158. // For heap enumeration
  159. ResetMarkFlags_HeapEnumeration = ResetMarkFlags_None,
  160. };
  161. enum RescanFlags
  162. {
  163. RescanFlags_None = 0x0,
  164. RescanFlags_ResetWriteWatch = 0x1
  165. };
  166. enum FindHeapObjectFlags
  167. {
  168. FindHeapObjectFlags_NoFlags = 0x0,
  169. FindHeapObjectFlags_ClearedAllocators = 0x1, // Assumes that the allocator is already cleared
  170. FindHeapObjectFlags_VerifyFreeBitForAttribute = 0x2, // Don't recompute the free bit vector if there is no pending objects, the attributes will always be correct
  171. FindHeapObjectFlags_NoFreeBitVerify = 0x4, // No checking whether the address is free or not.
  172. FindHeapObjectFlags_AllowInterior = 0x8, // Allow finding heap objects for interior pointers.
  173. };
  174. template <class TBlockAttributes> class SmallNormalHeapBlockT;
  175. template <class TBlockAttributes> class SmallLeafHeapBlockT;
  176. template <class TBlockAttributes> class SmallFinalizableHeapBlockT;
  177. #ifdef RECYCLER_WRITE_BARRIER
  178. template <class TBlockAttributes> class SmallNormalWithBarrierHeapBlockT;
  179. template <class TBlockAttributes> class SmallFinalizableWithBarrierHeapBlockT;
  180. #define INSTANTIATE_SWB_BLOCKTYPES(TemplateType) \
  181. template class TemplateType<Memory::SmallNormalWithBarrierHeapBlock>; \
  182. template class TemplateType<Memory::SmallFinalizableWithBarrierHeapBlock>; \
  183. template class TemplateType<Memory::MediumNormalWithBarrierHeapBlock>; \
  184. template class TemplateType<Memory::MediumFinalizableWithBarrierHeapBlock>; \
  185. #else
  186. #define INSTANTIATE_SWB_BLOCKTYPES(TemplateType)
  187. #endif
  188. #ifdef RECYCLER_VISITED_HOST
  189. template <class TBlockAttributes> class SmallRecyclerVisitedHostHeapBlockT;
  190. #define INSTANTIATE_RECYCLER_VISITED_BLOCKTYPES(TemplateType) \
  191. template class TemplateType<Memory::SmallRecyclerVisitedHostHeapBlock>; \
  192. template class TemplateType<Memory::MediumRecyclerVisitedHostHeapBlock>; \
  193. #else
  194. #define INSTANTIATE_RECYCLER_VISITED_BLOCKTYPES(TemplateType)
  195. #endif
  196. #define EXPLICIT_INSTANTIATE_WITH_SMALL_HEAP_BLOCK_TYPE(TemplateType) \
  197. template class TemplateType<Memory::SmallNormalHeapBlock>; \
  198. template class TemplateType<Memory::SmallLeafHeapBlock>; \
  199. template class TemplateType<Memory::SmallFinalizableHeapBlock>; \
  200. template class TemplateType<Memory::MediumNormalHeapBlock>; \
  201. template class TemplateType<Memory::MediumLeafHeapBlock>; \
  202. template class TemplateType<Memory::MediumFinalizableHeapBlock>; \
  203. INSTANTIATE_SWB_BLOCKTYPES(TemplateType) \
  204. INSTANTIATE_RECYCLER_VISITED_BLOCKTYPES(TemplateType) \
  205. class RecyclerHeapObjectInfo;
  206. class HeapBlock
  207. {
  208. public:
  209. enum HeapBlockType : byte
  210. {
  211. FreeBlockType = 0, // Only used in HeapBlockMap. Actual HeapBlock structures should never have this.
  212. SmallNormalBlockType,
  213. SmallLeafBlockType,
  214. SmallFinalizableBlockType,
  215. #ifdef RECYCLER_WRITE_BARRIER
  216. SmallNormalBlockWithBarrierType,
  217. SmallFinalizableBlockWithBarrierType,
  218. #endif
  219. #ifdef RECYCLER_VISITED_HOST
  220. SmallRecyclerVisitedHostBlockType,
  221. #endif
  222. MediumNormalBlockType,
  223. MediumLeafBlockType,
  224. MediumFinalizableBlockType,
  225. #ifdef RECYCLER_WRITE_BARRIER
  226. MediumNormalBlockWithBarrierType,
  227. MediumFinalizableBlockWithBarrierType,
  228. #endif
  229. #ifdef RECYCLER_VISITED_HOST
  230. MediumRecyclerVisitedHostBlockType,
  231. #endif
  232. LargeBlockType,
  233. #ifdef RECYCLER_VISITED_HOST
  234. SmallAllocBlockTypeCount = 7, // Actual number of types for blocks containing small allocations
  235. #else
  236. SmallAllocBlockTypeCount = 6,
  237. #endif
  238. #ifdef RECYCLER_VISITED_HOST
  239. MediumAllocBlockTypeCount = 6, // Actual number of types for blocks containing medium allocations
  240. #else
  241. MediumAllocBlockTypeCount = 5,
  242. #endif
  243. SmallBlockTypeCount = SmallAllocBlockTypeCount + MediumAllocBlockTypeCount, // Distinct block types independent of allocation size using SmallHeapBlockT
  244. LargeBlockTypeCount = 1, // There is only one LargeBlockType
  245. BlockTypeCount = SmallBlockTypeCount + LargeBlockTypeCount,
  246. };
  247. bool IsNormalBlock() const { return this->GetHeapBlockType() == SmallNormalBlockType || this->GetHeapBlockType() == MediumNormalBlockType; }
  248. bool IsLeafBlock() const { return this->GetHeapBlockType() == SmallLeafBlockType || this->GetHeapBlockType() == MediumLeafBlockType; }
  249. bool IsFinalizableBlock() const
  250. {
  251. return this->GetHeapBlockType() == SmallFinalizableBlockType || this->GetHeapBlockType() == MediumFinalizableBlockType
  252. #ifdef RECYCLER_VISITED_HOST
  253. || IsRecyclerVisitedHostBlock()
  254. #endif
  255. ;
  256. }
  257. #ifdef RECYCLER_VISITED_HOST
  258. bool IsRecyclerVisitedHostBlock() const { return this->GetHeapBlockType() == SmallRecyclerVisitedHostBlockType || this->GetHeapBlockType() == MediumRecyclerVisitedHostBlockType; }
  259. #endif
  260. #ifdef RECYCLER_WRITE_BARRIER
  261. bool IsAnyNormalBlock() const { return IsNormalBlock() || IsNormalWriteBarrierBlock(); }
  262. bool IsAnyFinalizableBlock() const { return IsFinalizableBlock() || IsFinalizableWriteBarrierBlock(); }
  263. bool IsNormalWriteBarrierBlock() const { return this->GetHeapBlockType() == SmallNormalBlockWithBarrierType || this->GetHeapBlockType() == MediumNormalBlockWithBarrierType; }
  264. bool IsFinalizableWriteBarrierBlock() const { return this->GetHeapBlockType() == SmallFinalizableBlockWithBarrierType || this->GetHeapBlockType() == MediumFinalizableBlockWithBarrierType; }
  265. #else
  266. bool IsAnyFinalizableBlock() const { return IsFinalizableBlock(); }
  267. bool IsAnyNormalBlock() const { return IsNormalBlock(); }
  268. #endif
  269. bool IsLargeHeapBlock() const { return this->GetHeapBlockType() == LargeBlockType; }
  270. char * GetAddress() const { return address; }
  271. Segment * GetSegment() const { return segment; }
  272. template <typename TBlockAttributes>
  273. SmallNormalHeapBlockT<TBlockAttributes> * AsNormalBlock();
  274. template <typename TBlockAttributes>
  275. SmallLeafHeapBlockT<TBlockAttributes> * AsLeafBlock();
  276. template <typename TBlockAttributes>
  277. SmallFinalizableHeapBlockT<TBlockAttributes> * AsFinalizableBlock();
  278. #ifdef RECYCLER_VISITED_HOST
  279. template <typename TBlockAttributes>
  280. SmallRecyclerVisitedHostHeapBlockT<TBlockAttributes> * AsRecyclerVisitedHostBlock();
  281. #endif
  282. #ifdef RECYCLER_WRITE_BARRIER
  283. template <typename TBlockAttributes>
  284. SmallNormalWithBarrierHeapBlockT<TBlockAttributes> * AsNormalWriteBarrierBlock();
  285. template <typename TBlockAttributes>
  286. SmallFinalizableWithBarrierHeapBlockT<TBlockAttributes> * AsFinalizableWriteBarrierBlock();
  287. #endif
  288. protected:
  289. char * address;
  290. Segment * segment;
  291. HeapBlockType const heapBlockType;
  292. bool needOOMRescan; // Set if we OOMed while marking a particular object
  293. #if ENABLE_CONCURRENT_GC
  294. bool isPendingConcurrentSweep;
  295. #if ENABLE_ALLOCATIONS_DURING_CONCURRENT_SWEEP
  296. // This flag is to identify whether this block was made available for allocations during the concurrent sweep and
  297. // still needs to be swept.
  298. bool isPendingConcurrentSweepPrep;
  299. #if DBG || defined(RECYCLER_SLOW_CHECK_ENABLED)
  300. // This flag ensures a block doesn't get swept more than once during a given sweep.
  301. bool hasFinishedSweepObjects;
  302. // When allocate from a block during concurrent sweep some checks need to be delayed until
  303. // the free and mark bits are rebuilt. This flag helps skip those validations until then.
  304. bool wasAllocatedFromDuringSweep;
  305. #endif
  306. #endif
  307. #endif
  308. public:
  309. template <bool doSpecialMark, typename Fn>
  310. bool UpdateAttributesOfMarkedObjects(MarkContext * markContext, void * objectAddress, size_t objectSize, unsigned char attributes, Fn fn);
  311. void SetNeedOOMRescan(Recycler * recycler);
  312. public:
  313. HeapBlock(HeapBlockType heapBlockType) :
  314. heapBlockType(heapBlockType),
  315. needOOMRescan(false)
  316. {
  317. static_assert(HeapBlockType::LargeBlockType == HeapBlockType::SmallBlockTypeCount, "LargeBlockType must come right after small+medium alloc block types");
  318. Assert(GetHeapBlockType() <= HeapBlock::HeapBlockType::BlockTypeCount);
  319. }
  320. HeapBlockType const GetHeapBlockType() const
  321. {
  322. return (heapBlockType);
  323. }
  324. #if (DBG || defined(RECYCLER_SLOW_CHECK_ENABLED)) && ENABLE_ALLOCATIONS_DURING_CONCURRENT_SWEEP
  325. bool WasAllocatedFromDuringSweep()
  326. {
  327. return this->wasAllocatedFromDuringSweep;
  328. }
  329. #endif
  330. IdleDecommitPageAllocator* GetPageAllocator(HeapInfo * heapInfo);
  331. bool GetAndClearNeedOOMRescan()
  332. {
  333. if (this->needOOMRescan)
  334. {
  335. this->needOOMRescan = false;
  336. return true;
  337. }
  338. return false;
  339. }
  340. #if DBG
  341. #if GLOBAL_ENABLE_WRITE_BARRIER
  342. virtual void WBSetBit(char* addr) = 0;
  343. virtual void WBSetBitRange(char* addr, uint count) = 0;
  344. virtual void WBClearBit(char* addr) = 0;
  345. virtual void WBVerifyBitIsSet(char* addr) = 0;
  346. virtual void WBClearObject(char* addr) = 0;
  347. #endif
  348. static void PrintVerifyMarkFailure(Recycler* recycler, char* objectAddress, char* target);
  349. #endif
  350. #if DBG
  351. virtual HeapInfo * GetHeapInfo() const = 0;
  352. virtual BOOL IsFreeObject(void* objectAddress) = 0;
  353. #endif
  354. virtual BOOL IsValidObject(void* objectAddress) = 0;
  355. virtual byte* GetRealAddressFromInterior(void* interiorAddress) = 0;
  356. virtual size_t GetObjectSize(void* object) const = 0;
  357. virtual bool FindHeapObject(void* objectAddress, Recycler * recycler, FindHeapObjectFlags flags, RecyclerHeapObjectInfo& heapObject) = 0;
  358. virtual bool TestObjectMarkedBit(void* objectAddress) = 0;
  359. virtual void SetObjectMarkedBit(void* objectAddress) = 0;
  360. #ifdef RECYCLER_VERIFY_MARK
  361. virtual bool VerifyMark(void * objectAddress, void * target) = 0;
  362. #endif
  363. #ifdef PROFILE_RECYCLER_ALLOC
  364. virtual void * GetTrackerData(void * address) = 0;
  365. virtual void SetTrackerData(void * address, void * data) = 0;
  366. #endif
  367. #if DBG || defined(RECYCLER_STATS) || defined(RECYCLER_PAGE_HEAP)
  368. bool isForceSweeping;
  369. #endif
  370. #ifdef RECYCLER_PERF_COUNTERS
  371. virtual void UpdatePerfCountersOnFree() = 0;
  372. #endif
  373. };
  374. #if ENABLE_ALLOCATIONS_DURING_CONCURRENT_SWEEP && SUPPORT_WIN32_SLIST
  375. template <typename TBlockType>
  376. struct HeapBlockSListItem {
  377. // SLIST_ENTRY needs to be the first element in the structure to avoid calculating offset with the SList API calls.
  378. SLIST_ENTRY itemEntry;
  379. TBlockType * itemHeapBlock;
  380. };
  381. #endif
  382. enum SweepMode
  383. {
  384. SweepMode_InThread,
  385. #if ENABLE_CONCURRENT_GC
  386. SweepMode_Concurrent,
  387. #if ENABLE_PARTIAL_GC
  388. SweepMode_ConcurrentPartial
  389. #endif
  390. #endif
  391. };
  392. // enum indicating the result of a sweep
  393. enum SweepState
  394. {
  395. SweepStateEmpty, // the block is completely empty and can be released
  396. SweepStateSwept, // the block is partially allocated, no object needs to be swept or finalized
  397. SweepStateFull, // the block is full, no object needs to be swept or finalized
  398. SweepStatePendingDispose, // the block has object that needs to be finalized
  399. #if ENABLE_CONCURRENT_GC
  400. SweepStatePendingSweep, // the block has object that needs to be swept
  401. #endif
  402. };
  403. template <class TBlockAttributes>
  404. class ValidPointers
  405. {
  406. public:
  407. ValidPointers(ushort const * validPointers, uint bucketIndex);
  408. ushort GetInteriorAddressIndex(uint index) const;
  409. ushort GetAddressIndex(uint index) const;
  410. private:
  411. #if USE_VPM_TABLE
  412. ushort const * validPointers;
  413. #endif
  414. #if !USE_VPM_TABLE || DBG
  415. uint indexPerObject;
  416. uint maxObjectIndex;
  417. static uint CalculateBucketInfo(uint bucketIndex, uint * stride);
  418. static ushort CalculateAddressIndex(uint index, uint indexPerObject, uint maxObjectIndex);
  419. static ushort CalculateInteriorAddressIndex(uint index, uint indexPerObject, uint maxObjectIndex);
  420. #endif
  421. };
  422. template <class TBlockAttributes>
  423. class SmallHeapBlockT : public HeapBlock
  424. {
  425. #ifdef ENABLE_DEBUG_CONFIG_OPTIONS
  426. friend class ::ScriptMemoryDumper;
  427. #endif
  428. template <typename TBlockType>
  429. friend class SmallHeapBlockAllocator;
  430. friend class HeapInfo;
  431. friend class RecyclerSweep;
  432. template <typename TBlockType>
  433. friend class SmallNormalHeapBucketBase;
  434. public:
  435. static const ushort InvalidAddressBit = 0xFFFF;
  436. typedef BVStatic<TBlockAttributes::BitVectorCount> SmallHeapBlockBitVector;
  437. struct BlockInfo
  438. {
  439. ushort lastObjectIndexOnPage;
  440. ushort pageObjectCount;
  441. };
  442. bool FindImplicitRootObject(void* candidate, Recycler* recycler, RecyclerHeapObjectInfo& heapObject);
  443. SmallHeapBlockT* next;
  444. FreeObject* freeObjectList;
  445. FreeObject* lastFreeObjectHead;
  446. ValidPointers<TBlockAttributes> validPointers;
  447. HeapBucket * heapBucket;
  448. // Review: Should GetBucketIndex return a short instead of an int?
  449. const uint bucketIndex;
  450. const ushort objectSize; // size in bytes
  451. const ushort objectCount;
  452. ushort freeCount;
  453. ushort lastFreeCount;
  454. ushort markCount;
  455. #if ENABLE_ALLOCATIONS_DURING_CONCURRENT_SWEEP
  456. #if DBG || defined(RECYCLER_SLOW_CHECK_ENABLED)
  457. // We need to keep track of the number of objects allocated during concurrent sweep, to be
  458. // able to make the correct determination about whether a block is EMPTY or FULL when the actual
  459. // sweep of this block happens.
  460. ushort objectsAllocatedDuringConcurrentSweepCount;
  461. ushort objectsMarkedDuringSweep;
  462. ushort lastObjectsAllocatedDuringConcurrentSweepCount;
  463. bool blockNotReusedInPartialHeapBlockList;
  464. bool blockNotReusedInPendingList;
  465. #endif
  466. #endif
  467. #if ENABLE_PARTIAL_GC
  468. ushort oldFreeCount;
  469. #endif
  470. bool isInAllocator;
  471. #if DBG
  472. bool isClearedFromAllocator;
  473. bool isIntegratedBlock;
  474. uint lastUncollectedAllocBytes;
  475. #endif
  476. SmallHeapBlockBitVector* markBits;
  477. SmallHeapBlockBitVector freeBits;
  478. #if DBG
  479. // TODO: (leish)(swb) move this to the block header if memory pressure on chk build is a problem
  480. // this causes 1/64 more memory usage on x64 or 1/32 more on x86
  481. BVStatic<TBlockAttributes::PageCount * AutoSystemInfo::PageSize / sizeof(void*)> wbVerifyBits;
  482. #endif
  483. #if DBG || defined(RECYCLER_STATS)
  484. SmallHeapBlockBitVector debugFreeBits;
  485. #endif
  486. #ifdef RECYCLER_MEMORY_VERIFY
  487. SmallHeapBlockBitVector explicitFreeBits;
  488. #endif
  489. bool IsFreeBitsValid() const
  490. {
  491. return this->freeObjectList == this->lastFreeObjectHead;
  492. }
  493. PageSegment * GetPageSegment() const { return (PageSegment *)GetSegment(); }
  494. public:
  495. ~SmallHeapBlockT();
  496. void ProtectUnusablePages() {}
  497. void RestoreUnusablePages() {}
  498. #if DBG && GLOBAL_ENABLE_WRITE_BARRIER
  499. virtual void WBVerifyBitIsSet(char* addr) override
  500. {
  501. uint index = (uint)(addr - this->address) / sizeof(void*);
  502. if (!wbVerifyBits.Test(index)) // TODO: (leish)(swb) need interlocked? seems not
  503. {
  504. PrintVerifyMarkFailure(this->GetRecycler(), addr, *(char**)addr);
  505. }
  506. }
  507. virtual void WBSetBit(char* addr) override
  508. {
  509. uint index = (uint)(addr - this->address) / sizeof(void*);
  510. wbVerifyBits.TestAndSetInterlocked(index);
  511. }
  512. virtual void WBSetBitRange(char* addr, uint count) override
  513. {
  514. uint index = (uint)(addr - this->address) / sizeof(void*);
  515. for (uint i = 0; i < count; i++)
  516. {
  517. wbVerifyBits.TestAndSetInterlocked(index + i);
  518. }
  519. }
  520. virtual void WBClearBit(char* addr) override
  521. {
  522. uint index = (uint)(addr - this->address) / sizeof(void*);
  523. wbVerifyBits.TestAndClearInterlocked(index);
  524. }
  525. virtual void WBClearObject(char* addr) override
  526. {
  527. Assert((uint)(addr - this->address) % this->objectSize == 0);
  528. uint index = (uint)(addr - this->address) / sizeof(void*);
  529. uint count = (uint)(this->objectSize / sizeof(void*));
  530. for (uint i = 0; i < count; i++)
  531. {
  532. wbVerifyBits.TestAndClearInterlocked(index + i);
  533. }
  534. }
  535. #endif
  536. uint GetUnusablePageCount()
  537. {
  538. return 0;
  539. }
  540. #ifdef RECYCLER_WRITE_BARRIER
  541. bool IsWithBarrier() const;
  542. #endif
  543. void RemoveFromHeapBlockMap(Recycler* recycler);
  544. char* GetAddress() const { return address; }
  545. char * GetEndAddress() const { return address + (this->GetPageCount() * AutoSystemInfo::PageSize); }
  546. uint GetObjectWordCount() const { return this->objectSize / sizeof(void *); }
  547. uint GetPageCount() const;
  548. bool HasFreeObject() const
  549. {
  550. return freeObjectList != nullptr;
  551. }
  552. bool IsInAllocator() const;
  553. bool HasPendingDisposeObjects();
  554. bool HasAnyDisposeObjects();
  555. #if DBG
  556. void VerifyMarkBitVector();
  557. bool IsClearedFromAllocator() const;
  558. void SetIsClearedFromAllocator(bool value);
  559. void SetIsIntegratedBlock() { this->isIntegratedBlock = true; }
  560. #endif
  561. #ifdef RECYCLER_MEMORY_VERIFY
  562. void SetExplicitFreeBitForObject(void* object);
  563. void ClearExplicitFreeBitForObject(void* object);
  564. #endif
  565. #ifdef RECYCLER_STRESS
  566. void InduceFalsePositive(Recycler * recycler);
  567. #endif
  568. #if ENABLE_MEM_STATS
  569. void AggregateBlockStats(HeapBucketStats& stats, bool isAllocatorBlock = false, FreeObject* freeObjectList = nullptr, bool isBumpAllocated = false);
  570. #endif
  571. /*
  572. * Quick description of the bit vectors
  573. *
  574. * The free bit vector is created by EnsureFreeBitVector. It's created by walking through the free list
  575. * for the heap block and setting the corresponding bit indices in the bit vector.
  576. *
  577. * The mark bit vector is more complicated. In most cases, it represents the objects that are alive (marked)
  578. * + the objects the are free (i.e in the free list). This is so that when we sweep, we don't bother sweeping over objects
  579. * that are already in the free list, we sweep over objects that were allocated and no longer alive since the last GC.
  580. * However, during rescan, the mark bit vector represents the objects that are actually alive. We set the marked bit
  581. * vector to this state before calling RescanObjects, so that we scan through only the objects that are actually alive.
  582. * This means that we don't rescan newly allocated objects during rescan, because rescan doesn't change add new mark bits.
  583. * Instead, these objects are marked after rescan during in-thread mark if they're actually alive.
  584. */
  585. SmallHeapBlockBitVector * GetMarkedBitVector() { return markBits; }
  586. SmallHeapBlockBitVector * GetFreeBitVector() { return &freeBits; }
  587. SmallHeapBlockBitVector const * GetInvalidBitVector();
  588. BlockInfo const * GetBlockInfo();
  589. ushort GetObjectBitDelta();
  590. static uint GetObjectBitDeltaForBucketIndex(uint bucketIndex);
  591. static char* GetBlockStartAddress(char* address)
  592. {
  593. uintptr_t mask = ~((TBlockAttributes::PageCount * AutoSystemInfo::PageSize) - 1);
  594. return (char*)((uintptr_t)address & mask);
  595. }
  596. bool IsValidBitIndex(uint bitIndex)
  597. {
  598. Assert(bitIndex < TBlockAttributes::BitVectorCount);
  599. return bitIndex % GetObjectBitDelta() == 0;
  600. }
  601. void MarkImplicitRoots();
  602. void SetNextBlock(SmallHeapBlockT * next) { this->next=next; }
  603. SmallHeapBlockT * GetNextBlock() const { return next; }
  604. uint GetObjectSize() const { return objectSize; }
  605. uint GetObjectCount() const { return objectCount; }
  606. uint GetMarkedCount() const { return markCount; }
  607. // Valid during sweep time
  608. ushort GetExpectedFreeObjectCount() const;
  609. uint GetExpectedFreeBytes() const;
  610. ushort GetExpectedSweepObjectCount() const;
  611. #if DBG || defined(RECYCLER_STATS)
  612. SmallHeapBlockBitVector * GetDebugFreeBitVector() { return &debugFreeBits; }
  613. #endif
  614. #if DBG
  615. virtual HeapInfo * GetHeapInfo() const override;
  616. virtual BOOL IsFreeObject(void* objectAddress) override;
  617. #endif
  618. virtual BOOL IsValidObject(void* objectAddress) override;
  619. byte* GetRealAddressFromInterior(void* interiorAddress) override sealed;
  620. bool TestObjectMarkedBit(void* objectAddress) override sealed;
  621. void SetObjectMarkedBit(void* objectAddress) override;
  622. virtual size_t GetObjectSize(void* object) const override { return objectSize; }
  623. uint GetMarkCountForSweep();
  624. #if ENABLE_ALLOCATIONS_DURING_CONCURRENT_SWEEP
  625. #if DBG || defined(RECYCLER_SLOW_CHECK_ENABLED)
  626. void ResetConcurrentSweepAllocationCounts();
  627. #endif
  628. #endif
  629. SweepState Sweep(RecyclerSweep& recyclerSweep, bool queuePendingSweep, bool allocable, ushort finalizeCount = 0, bool hasPendingDispose = false);
  630. template <SweepMode mode>
  631. void SweepObjects(Recycler * recycler);
  632. uint GetAndClearLastFreeCount();
  633. void ClearAllAllocBytes(); // Reset all unaccounted alloc bytes and the new alloc count
  634. #if ENABLE_PARTIAL_GC
  635. uint GetAndClearUnaccountedAllocBytes();
  636. void AdjustPartialUncollectedAllocBytes(RecyclerSweep& recyclerSweep, uint const expectSweepCount);
  637. bool DoPartialReusePage(RecyclerSweep const& recyclerSweep, uint& expectFreeByteCount);
  638. #if DBG || defined(RECYCLER_STATS)
  639. void SweepVerifyPartialBlock(Recycler * recycler);
  640. #endif
  641. #endif
  642. void TransferProcessedObjects(FreeObject * list, FreeObject * tail);
  643. BOOL ReassignPages(Recycler * recycler);
  644. BOOL SetPage(__in_ecount_pagesize char * baseAddress, PageSegment * pageSegment, Recycler * recycler);
  645. void ReleasePages(Recycler * recycler);
  646. void ReleasePagesSweep(Recycler * recycler);
  647. void ReleasePagesShutdown(Recycler * recycler);
  648. #if ENABLE_BACKGROUND_PAGE_FREEING
  649. void BackgroundReleasePagesSweep(Recycler* recycler);
  650. #endif
  651. void Reset();
  652. void EnumerateObjects(ObjectInfoBits infoBits, void (*CallBackFunction)(void * address, size_t size));
  653. bool IsImplicitRoot(uint objectIndex)
  654. {
  655. return (this->ObjectInfo(objectIndex) & ImplicitRootBit) != 0;
  656. }
  657. #ifdef RECYCLER_SLOW_CHECK_ENABLED
  658. void Check(bool expectFull, bool expectPending);
  659. #endif
  660. #ifdef RECYCLER_MEMORY_VERIFY
  661. void Verify(bool pendingDispose = false);
  662. void VerifyBumpAllocated(_In_ char * bumpAllocatedAddres);
  663. #endif
  664. #ifdef RECYCLER_VERIFY_MARK
  665. void VerifyMark();
  666. virtual bool VerifyMark(void * objectAddress, void * target) override;
  667. #endif
  668. #ifdef RECYCLER_PERF_COUNTERS
  669. virtual void UpdatePerfCountersOnFree() override sealed;
  670. #endif
  671. #ifdef PROFILE_RECYCLER_ALLOC
  672. virtual void * GetTrackerData(void * address) override;
  673. virtual void SetTrackerData(void * address, void * data) override;
  674. #endif
  675. static ushort GetAddressBitIndex(void * objectAddress);
  676. static void * GetRealAddressFromInterior(void * objectAddress, uint objectSize, byte bucketIndex);
  677. protected:
  678. static size_t GetAllocPlusSize(uint objectCount);
  679. inline void SetAttributes(void * address, unsigned char attributes);
  680. ushort GetAddressIndex(void * objectAddress);
  681. SmallHeapBlockT(HeapBucket * bucket, ushort objectSize, ushort objectCount, HeapBlockType heapBlockType);
  682. ushort GetInteriorAddressIndex(void * interiorAddress);
  683. ushort GetObjectIndexFromBitIndex(ushort bitIndex);
  684. template <SweepMode mode>
  685. void SweepObject(Recycler * recycler, uint index, void * addr);
  686. void EnqueueProcessedObject(FreeObject **list, void * objectAddress, uint index);
  687. void EnqueueProcessedObject(FreeObject **list, FreeObject ** tail, void * objectAddress, uint index);
  688. #ifdef RECYCLER_SLOW_CHECK_ENABLED
  689. template <typename TBlockType>
  690. bool GetFreeObjectListOnAllocatorImpl(FreeObject ** freeObjectList);
  691. virtual bool GetFreeObjectListOnAllocator(FreeObject ** freeObjectList) = 0;
  692. void CheckDebugFreeBitVector(bool isCollecting);
  693. void CheckFreeBitVector(bool isCollecting);
  694. #endif
  695. SmallHeapBlockBitVector * EnsureFreeBitVector(bool isCollecting = true);
  696. SmallHeapBlockBitVector * BuildFreeBitVector();
  697. ushort BuildFreeBitVector(SmallHeapBlockBitVector * bv);
  698. BOOL IsInFreeObjectList(void * objectAddress);
  699. void ClearObjectInfoList();
  700. byte& ObjectInfo(uint index);
  701. inline void FillFreeMemory(__in_bcount(size) void * address, size_t size);
  702. template <typename TBlockType>
  703. bool FindHeapObjectImpl(void* objectAddress, Recycler * recycler, FindHeapObjectFlags flags, RecyclerHeapObjectInfo& heapObject);
  704. protected:
  705. IdleDecommitPageAllocator * GetPageAllocator();
  706. void Init(ushort objectSize, ushort objectCount);
  707. void ConstructorCommon(HeapBucket * bucket, ushort objectSize, ushort objectCount, HeapBlockType heapBlockType);
  708. template <typename Fn>
  709. void ForEachAllocatedObject(Fn fn);
  710. template <typename Fn>
  711. void ForEachAllocatedObject(ObjectInfoBits attributes, Fn fn);
  712. template <typename Fn>
  713. void ScanNewImplicitRootsBase(Fn fn);
  714. // This is public for code readability but this
  715. // returns a value only on debug builds. On retail builds
  716. // this returns null
  717. Recycler * GetRecycler() const;
  718. #if DBG
  719. uint GetMarkCountOnHeapBlockMap() const;
  720. #endif
  721. private:
  722. #ifdef PROFILE_RECYCLER_ALLOC
  723. void ** GetTrackerDataArray();
  724. #endif
  725. };
  726. // Forward declare specializations
  727. template<>
  728. SmallHeapBlockT<MediumAllocationBlockAttributes>::SmallHeapBlockT(HeapBucket * bucket, ushort objectSize, ushort objectCount, HeapBlockType heapBlockType);
  729. template <>
  730. uint
  731. SmallHeapBlockT<MediumAllocationBlockAttributes>::GetObjectBitDeltaForBucketIndex(uint bucketIndex);
  732. template <>
  733. uint
  734. SmallHeapBlockT<MediumAllocationBlockAttributes>::GetUnusablePageCount();
  735. template <>
  736. void
  737. SmallHeapBlockT<MediumAllocationBlockAttributes>::ProtectUnusablePages();
  738. template <>
  739. void
  740. SmallHeapBlockT<MediumAllocationBlockAttributes>::RestoreUnusablePages();
  741. // Declare the class templates
  742. typedef SmallHeapBlockT<SmallAllocationBlockAttributes> SmallHeapBlock;
  743. typedef SmallHeapBlockT<MediumAllocationBlockAttributes> MediumHeapBlock;
  744. extern template class SmallHeapBlockT<SmallAllocationBlockAttributes>;
  745. extern template class SmallHeapBlockT<MediumAllocationBlockAttributes>;
  746. extern template class ValidPointers<SmallAllocationBlockAttributes>;
  747. extern template class ValidPointers<MediumAllocationBlockAttributes>;
  748. class HeapBlockList
  749. {
  750. public:
  751. template <typename TBlockType, typename Fn>
  752. static void ForEach(TBlockType * list, Fn fn)
  753. {
  754. ForEach<TBlockType, Fn>(list, nullptr, fn);
  755. }
  756. template <typename TBlockType, typename Fn>
  757. static void ForEach(TBlockType * list, TBlockType * tail, Fn fn)
  758. {
  759. TBlockType * heapBlock = list;
  760. while (heapBlock != tail)
  761. {
  762. fn(heapBlock);
  763. heapBlock = heapBlock->GetNextBlock();
  764. }
  765. }
  766. template <typename TBlockType, typename Fn>
  767. static void ForEachEditing(TBlockType * list, Fn fn)
  768. {
  769. ForEachEditing<TBlockType, Fn>(list, nullptr, fn);
  770. };
  771. template <typename TBlockType, typename Fn>
  772. static void ForEachEditing(TBlockType * list, TBlockType * tail, Fn fn)
  773. {
  774. TBlockType * heapBlock = list;
  775. while (heapBlock != tail)
  776. {
  777. TBlockType * nextBlock = heapBlock->GetNextBlock();
  778. fn(heapBlock);
  779. heapBlock = nextBlock;
  780. }
  781. };
  782. template <typename TBlockType>
  783. static size_t Count(TBlockType * list)
  784. {
  785. size_t currentHeapBlockCount = 0;
  786. HeapBlockList::ForEach(list, [&currentHeapBlockCount](TBlockType * heapBlock)
  787. {
  788. currentHeapBlockCount++;
  789. });
  790. return currentHeapBlockCount;
  791. };
  792. template <typename TBlockType>
  793. static TBlockType * Tail(TBlockType * list)
  794. {
  795. TBlockType * tail = nullptr;
  796. HeapBlockList::ForEach(list, [&tail](TBlockType * heapBlock)
  797. {
  798. tail = heapBlock;
  799. });
  800. return tail;
  801. }
  802. #if DBG
  803. template <typename TBlockType>
  804. static bool Contains(TBlockType * block, TBlockType * list, TBlockType * tail = nullptr)
  805. {
  806. TBlockType * heapBlock = list;
  807. while (heapBlock != tail)
  808. {
  809. if (heapBlock == block)
  810. {
  811. return true;
  812. }
  813. heapBlock = heapBlock->GetNextBlock();
  814. }
  815. return false;
  816. }
  817. #endif
  818. };
  819. }