HeapBlock.h 35 KB

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