Nursery.h (28155B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- 2 * vim: set ts=8 sw=2 et tw=80: 3 * 4 * This Source Code Form is subject to the terms of the Mozilla Public 5 * License, v. 2.0. If a copy of the MPL was not distributed with this file, 6 * You can obtain one at http://mozilla.org/MPL/2.0/. */ 7 8 #ifndef gc_Nursery_h 9 #define gc_Nursery_h 10 11 #include "mozilla/EnumeratedArray.h" 12 #include "mozilla/TimeStamp.h" 13 14 #include <tuple> 15 16 #include "ds/LifoAlloc.h" 17 #include "ds/SlimLinkedList.h" 18 #include "gc/Allocator.h" 19 #include "gc/GCEnum.h" 20 #include "gc/GCProbes.h" 21 #include "gc/Heap.h" 22 #include "gc/Pretenuring.h" 23 #include "js/AllocPolicy.h" 24 #include "js/Class.h" 25 #include "js/GCAPI.h" 26 #include "js/GCVector.h" 27 #include "js/HeapAPI.h" 28 #include "js/TypeDecls.h" 29 #include "js/UniquePtr.h" 30 #include "js/Utility.h" 31 #include "js/Vector.h" 32 33 #define FOR_EACH_NURSERY_PROFILE_TIME(_) \ 34 /* Key Header text */ \ 35 _(Total, "total") \ 36 _(TraceValues, "mkVals") \ 37 _(TraceCells, "mkClls") \ 38 _(TraceSlots, "mkSlts") \ 39 _(TraceWasmAnyRefs, "mkWars") \ 40 _(TraceWholeCells, "mcWCll") \ 41 _(TraceGenericEntries, "mkGnrc") \ 42 _(CheckHashTables, "ckTbls") \ 43 _(MarkRuntime, "mkRntm") \ 44 _(MarkDebugger, "mkDbgr") \ 45 _(TraceWeakMaps, "trWkMp") \ 46 _(SweepCaches, "swpCch") \ 47 _(CollectToObjFP, "colObj") \ 48 _(CollectToStrFP, "colStr") \ 49 _(ObjectsTenuredCallback, "tenCB") \ 50 _(Sweep, "sweep") \ 51 _(UpdateJitActivations, "updtIn") \ 52 _(FreeMallocedBuffers, "frSlts") \ 53 _(ClearNursery, "clear") \ 54 _(PurgeStringToAtomCache, "pStoA") \ 55 _(Pretenure, "pretnr") 56 57 template <typename T> 58 class SharedMem; 59 60 namespace mozilla { 61 class StringBuffer; 62 }; 63 64 namespace js { 65 66 struct StringStats; 67 class AutoLockGCBgAlloc; 68 class ObjectElements; 69 struct NurseryChunk; 70 class HeapSlot; 71 class JSONPrinter; 72 class MapObject; 73 class NurseryDecommitTask; 74 class NurserySweepTask; 75 class SetObject; 76 class JS_PUBLIC_API Sprinter; 77 class WeakMapBase; 78 79 namespace gc { 80 81 class AutoGCSession; 82 struct Cell; 83 class GCSchedulingTunables; 84 struct LargeBuffer; 85 class StoreBuffer; 86 class TenuringTracer; 87 88 } // namespace gc 89 90 class Nursery { 91 public: 92 explicit Nursery(gc::GCRuntime* gc); 93 ~Nursery(); 94 95 [[nodiscard]] bool init(AutoLockGCBgAlloc& lock); 96 97 void enable(); 98 void disable(); 99 bool isEnabled() const { return capacity() != 0; } 100 101 void enableStrings(); 102 void disableStrings(); 103 bool canAllocateStrings() const { return canAllocateStrings_; } 104 105 void enableBigInts(); 106 void disableBigInts(); 107 bool canAllocateBigInts() const { return canAllocateBigInts_; } 108 109 void setSemispaceEnabled(bool enabled); 110 bool semispaceEnabled() const { return semispaceEnabled_; } 111 112 // Return true if no allocations have been made since the last collection. 113 bool isEmpty() const; 114 115 // Check whether an arbitrary pointer is within the nursery. This is 116 // slower than IsInsideNursery(Cell*), but works on all types of pointers. 117 bool isInside(gc::Cell* cellp) const = delete; 118 inline bool isInside(const void* p) const; 119 120 template <typename T> 121 inline bool isInside(const SharedMem<T>& p) const; 122 123 // Allocate and return a pointer to a new GC thing. Returns nullptr if the 124 // Nursery is full. 125 void* allocateCell(gc::AllocSite* site, size_t size, JS::TraceKind kind); 126 127 // Allocate and return a pointer to a new GC thing. Returns nullptr if the 128 // handleAllocationFailure() needs to be called before retrying. 129 inline void* tryAllocateCell(gc::AllocSite* site, size_t size, 130 JS::TraceKind kind); 131 132 // Attempt to handle the failure of tryAllocate. Returns a GCReason if minor 133 // GC is required, or NO_REASON if the failure was handled and allocation will 134 // now succeed. 135 [[nodiscard]] JS::GCReason handleAllocationFailure(); 136 137 static size_t nurseryCellHeaderSize() { 138 return sizeof(gc::NurseryCellHeader); 139 } 140 141 // Allocate a buffer for a given zone, using the nursery if possible. Returns 142 // <buffer, isMalloced> so the caller can register the buffer if 143 // needed. Returns false in |isMalloced| if the allocation fails. 144 // 145 // Use the following API if the owning Cell is already known. 146 std::tuple<void*, bool> allocNurseryOrMallocBuffer(JS::Zone* zone, 147 size_t nbytes, 148 arena_id_t arenaId); 149 void* allocateInternalBuffer(JS::Zone* zone, size_t nbytes); 150 151 // Like allocNurseryOrMallocBuffer, but returns nullptr if the buffer can't 152 // be allocated in the nursery. 153 void* tryAllocateNurseryBuffer(JS::Zone* zone, size_t nbytes, 154 arena_id_t arenaId); 155 156 // Allocate a buffer for a given Cell, using the nursery if possible and 157 // owner is in the nursery. 158 void* allocNurseryOrMallocBuffer(JS::Zone* zone, gc::Cell* owner, 159 size_t nbytes, arena_id_t arenaId); 160 void* allocateBuffer(JS::Zone* zone, gc::Cell* owner, size_t nbytes, 161 size_t maxNurserySize); 162 163 // Allocate a zero-initialized buffer for a given zone, using the nursery if 164 // possible. If the buffer isn't allocated in the nursery, the given arena is 165 // used. Returns <buffer, isMalloced>. Returns false in |isMalloced| if the 166 // allocation fails. 167 std::tuple<void*, bool> allocateZeroedBuffer(JS::Zone* zone, size_t nbytes, 168 arena_id_t arena); 169 170 // Allocate a zero-initialized buffer for a given Cell, using the nursery if 171 // possible and |owner| is in the nursery. If the buffer isn't allocated in 172 // the nursery, the given arena is used. 173 void* allocateZeroedBuffer(gc::Cell* owner, size_t nbytes, arena_id_t arena); 174 175 // Resize an existing buffer. 176 void* reallocNurseryOrMallocBuffer(JS::Zone* zone, gc::Cell* cell, 177 void* oldBuffer, size_t oldBytes, 178 size_t newBytes, arena_id_t arena); 179 180 // Resize an existing buffer. 181 void* reallocateBuffer(JS::Zone* zone, gc::Cell* cell, void* oldBuffer, 182 size_t oldBytes, size_t newBytes, 183 size_t maxNurserySize); 184 185 // Free an existing buffer. 186 void freeBuffer(JS::Zone* zone, gc::Cell* cell, void* buffer, size_t bytes); 187 188 // The maximum number of bytes allowed to reside in nursery buffers. 189 static const size_t MaxNurseryBufferSize = 1024; 190 191 // Do a minor collection. 192 void collect(JS::GCOptions options, JS::GCReason reason); 193 194 // If the thing at |*ref| in the Nursery has been forwarded, set |*ref| to 195 // the new location and return true. Otherwise return false and leave 196 // |*ref| unset. 197 [[nodiscard]] MOZ_ALWAYS_INLINE static bool getForwardedPointer( 198 js::gc::Cell** ref); 199 200 // Forward a slots/elements pointer stored in an Ion frame. 201 void forwardBufferPointer(uintptr_t* pSlotsElems); 202 203 inline void maybeSetForwardingPointer(JSTracer* trc, void* oldData, 204 void* newData, bool direct); 205 inline void setForwardingPointerWhileTenuring(void* oldData, void* newData, 206 bool direct); 207 208 // Handle an external buffer when a cell is promoted. Updates the pointer to 209 // the (possibly moved) buffer and returns whether it was moved. 210 // bytesUsed can be less than bytesCapacity if not all bytes need to be copied 211 // when the buffer is moved. 212 enum WasBufferMoved : bool { BufferNotMoved = false, BufferMoved = true }; 213 WasBufferMoved maybeMoveRawNurseryOrMallocBufferOnPromotion( 214 void** bufferp, gc::Cell* owner, size_t bytesUsed, size_t bytesCapacity, 215 MemoryUse use, arena_id_t arena); 216 template <typename T> 217 WasBufferMoved maybeMoveNurseryOrMallocBufferOnPromotion( 218 T** bufferp, gc::Cell* owner, size_t bytesUsed, size_t bytesCapacity, 219 MemoryUse use, arena_id_t arena) { 220 return maybeMoveRawNurseryOrMallocBufferOnPromotion( 221 reinterpret_cast<void**>(bufferp), owner, bytesUsed, bytesCapacity, use, 222 arena); 223 } 224 template <typename T> 225 WasBufferMoved maybeMoveNurseryOrMallocBufferOnPromotion(T** bufferp, 226 gc::Cell* owner, 227 size_t nbytes, 228 MemoryUse use) { 229 return maybeMoveNurseryOrMallocBufferOnPromotion(bufferp, owner, nbytes, 230 nbytes, use, MallocArena); 231 } 232 233 WasBufferMoved maybeMoveRawBufferOnPromotion(void** bufferp, gc::Cell* owner, 234 size_t nbytes); 235 template <typename T> 236 WasBufferMoved maybeMoveBufferOnPromotion(T** bufferp, gc::Cell* owner, 237 size_t nbytes) { 238 return maybeMoveRawBufferOnPromotion(reinterpret_cast<void**>(bufferp), 239 owner, nbytes); 240 } 241 242 // Register a malloced buffer that is held by a nursery object, which 243 // should be freed at the end of a minor GC. Buffers are unregistered when 244 // their owning objects are tenured. 245 [[nodiscard]] bool registerMallocedBuffer(void* buffer, size_t nbytes); 246 247 // Mark a malloced buffer as no longer needing to be freed. 248 inline void removeMallocedBuffer(void* buffer, size_t nbytes); 249 250 // Mark a malloced buffer as no longer needing to be freed during minor 251 // GC. There's no need to account for the size here since all remaining 252 // buffers will soon be freed. 253 inline void removeMallocedBufferDuringMinorGC(void* buffer); 254 255 [[nodiscard]] bool addedUniqueIdToCell(gc::Cell* cell) { 256 MOZ_ASSERT(IsInsideNursery(cell)); 257 MOZ_ASSERT(isEnabled()); 258 return cellsWithUid_.append(cell); 259 } 260 261 [[nodiscard]] inline bool addStringBuffer(JSLinearString* s); 262 263 [[nodiscard]] inline bool addExtensibleStringBuffer( 264 JSLinearString* s, mozilla::StringBuffer* buffer, 265 bool updateMallocBytes = true); 266 inline void removeExtensibleStringBuffer(JSLinearString* s, 267 bool updateMallocBytes = true); 268 269 size_t sizeOfMallocedBuffers(mozilla::MallocSizeOf mallocSizeOf) const; 270 271 size_t totalCapacity() const; 272 size_t totalCommitted() const; 273 274 #ifdef JS_GC_ZEAL 275 void enterZealMode(); 276 void leaveZealMode(); 277 #endif 278 279 // Write profile time JSON on JSONPrinter. 280 void renderProfileJSON(JSONPrinter& json) const; 281 282 // Print header line for profile times. 283 void printProfileHeader(); 284 285 // Print total profile times on shutdown. 286 void printTotalProfileTimes(); 287 288 void* addressOfPosition() const { return (void**)&toSpace.position_; } 289 static constexpr int32_t offsetOfCurrentEndFromPosition() { 290 return offsetof(Nursery, toSpace.currentEnd_) - 291 offsetof(Nursery, toSpace.position_); 292 } 293 294 void* addressOfNurseryAllocatedSites() { 295 return pretenuringNursery.addressOfAllocatedSites(); 296 } 297 298 void requestMinorGC(JS::GCReason reason); 299 300 bool minorGCRequested() const { 301 return minorGCTriggerReason_ != JS::GCReason::NO_REASON; 302 } 303 JS::GCReason minorGCTriggerReason() const { return minorGCTriggerReason_; } 304 305 bool wantEagerCollection() const; 306 307 bool enableProfiling() const { return enableProfiling_; } 308 309 bool addMapWithNurseryIterators(MapObject* obj) { 310 MOZ_ASSERT_IF(!mapsWithNurseryIterators_.empty(), 311 mapsWithNurseryIterators_.back() != obj); 312 return mapsWithNurseryIterators_.append(obj); 313 } 314 bool addSetWithNurseryIterators(SetObject* obj) { 315 MOZ_ASSERT_IF(!setsWithNurseryIterators_.empty(), 316 setsWithNurseryIterators_.back() != obj); 317 return setsWithNurseryIterators_.append(obj); 318 } 319 bool addWeakMapWithNurseryEntries(WeakMapBase* wm) { 320 MOZ_ASSERT_IF(!weakMapsWithNurseryEntries_.empty(), 321 weakMapsWithNurseryEntries_.back() != wm); 322 return weakMapsWithNurseryEntries_.append(wm); 323 } 324 325 void joinSweepTask(); 326 void joinDecommitTask(); 327 328 #ifdef DEBUG 329 bool sweepTaskIsIdle(); 330 #endif 331 332 mozilla::TimeStamp collectionStartTime() { 333 return startTimes_[ProfileKey::Total]; 334 } 335 336 bool canCreateAllocSite() { return pretenuringNursery.canCreateAllocSite(); } 337 void noteAllocSiteCreated() { pretenuringNursery.noteAllocSiteCreated(); } 338 bool reportPretenuring() const { return pretenuringReportFilter_.enabled; } 339 void maybeStopPretenuring(gc::GCRuntime* gc) { 340 pretenuringNursery.maybeStopPretenuring(gc); 341 } 342 343 void setAllocFlagsForZone(JS::Zone* zone); 344 345 bool shouldTenureEverything(JS::GCReason reason); 346 347 inline bool inCollectedRegion(const gc::Cell* cell) const; 348 inline bool inCollectedRegion(void* ptr) const; 349 350 void trackMallocedBufferOnPromotion(void* buffer, gc::Cell* owner, 351 size_t nbytes, MemoryUse use); 352 void trackBufferOnPromotion(void* buffer, gc::Cell* owner, size_t nbytes); 353 354 // Round a size in bytes to the nearest valid nursery size. 355 static size_t roundSize(size_t size); 356 357 inline void addMallocedBufferBytes(size_t nbytes); 358 inline void removeMallocedBufferBytes(size_t nbytes); 359 360 mozilla::TimeStamp lastCollectionEndTime() const; 361 362 size_t capacity() const { return capacity_; } 363 364 private: 365 struct Space; 366 367 enum class ProfileKey { 368 #define DEFINE_TIME_KEY(name, text) name, 369 FOR_EACH_NURSERY_PROFILE_TIME(DEFINE_TIME_KEY) 370 #undef DEFINE_TIME_KEY 371 KeyCount 372 }; 373 374 using ProfileTimes = mozilla::EnumeratedArray<ProfileKey, mozilla::TimeStamp, 375 size_t(ProfileKey::KeyCount)>; 376 using ProfileDurations = 377 mozilla::EnumeratedArray<ProfileKey, mozilla::TimeDuration, 378 size_t(ProfileKey::KeyCount)>; 379 380 // Total number of chunks and the capacity of the current nursery 381 // space. Chunks will be lazily allocated and added to the chunks array up to 382 // this limit. After that the nursery must be collected. This limit may be 383 // changed at the end of collection by maybeResizeNursery. 384 uint32_t maxChunkCount() const { 385 MOZ_ASSERT(toSpace.maxChunkCount_); 386 return toSpace.maxChunkCount_; 387 } 388 389 // Number of allocated (ready to use) chunks. 390 unsigned allocatedChunkCount() const { return toSpace.chunks_.length(); } 391 392 uint32_t currentChunk() const { return toSpace.currentChunk_; } 393 uint32_t startChunk() const { return toSpace.startChunk_; } 394 uintptr_t startPosition() const { return toSpace.startPosition_; } 395 396 // Used and free space both include chunk headers for that part of the 397 // nursery. 398 MOZ_ALWAYS_INLINE size_t usedSpace() const { 399 return capacity() - freeSpace(); 400 } 401 MOZ_ALWAYS_INLINE size_t freeSpace() const { 402 MOZ_ASSERT(isEnabled()); 403 MOZ_ASSERT(currentChunk() < maxChunkCount()); 404 return (currentEnd() - position()) + 405 (maxChunkCount() - currentChunk() - 1) * gc::ChunkSize; 406 } 407 408 // Calculate the promotion rate of the most recent minor GC. 409 // The valid_for_tenuring parameter is used to return whether this 410 // promotion rate is accurate enough (the nursery was full enough) to be 411 // used for tenuring and other decisions. 412 // 413 // Must only be called if the previousGC data is initialised. 414 double calcPromotionRate(bool* validForTenuring) const; 415 416 NurseryChunk& chunk(unsigned index) const { return *toSpace.chunks_[index]; } 417 418 // Set the allocation position to the start of a chunk. This sets 419 // currentChunk_, position_ and currentEnd_ values as appropriate. 420 void moveToStartOfChunk(unsigned chunkno); 421 422 bool initFirstChunk(AutoLockGCBgAlloc& lock); 423 void setCapacity(size_t newCapacity); 424 425 void poisonAndInitCurrentChunk(); 426 427 void setCurrentEnd(); 428 void setStartToCurrentPosition(); 429 430 // Allocate another chunk. 431 [[nodiscard]] bool allocateNextChunk(AutoLockGCBgAlloc& lock); 432 433 uintptr_t position() const { return toSpace.position_; } 434 uintptr_t currentEnd() const { return toSpace.currentEnd_; } 435 436 MOZ_ALWAYS_INLINE bool isSubChunkMode() const; 437 438 JSRuntime* runtime() const; 439 gcstats::Statistics& stats() const; 440 441 const js::gc::GCSchedulingTunables& tunables() const; 442 443 void getAllocFlagsForZone(JS::Zone* zone, bool* allocObjectsOut, 444 bool* allocStringsOut, bool* allocBigIntsOut, 445 bool* allocGetterSettersOut); 446 void updateAllZoneAllocFlags(); 447 void updateAllocFlagsForZone(JS::Zone* zone); 448 void discardCodeAndSetJitFlagsForZone(JS::Zone* zone); 449 450 void* allocate(size_t size); 451 452 // Common internal allocator function. If this fails, call 453 // handleAllocationFailure to see whether it's possible to retry. 454 inline void* tryAllocate(size_t size); 455 456 [[nodiscard]] bool moveToNextChunk(); 457 458 bool freeSpaceIsBelowEagerThreshold() const; 459 bool isUnderused() const; 460 461 struct CollectionResult { 462 size_t tenuredBytes; 463 size_t tenuredCells; 464 }; 465 CollectionResult doCollection(gc::AutoGCSession& session, 466 JS::GCOptions options, JS::GCReason reason); 467 void swapSpaces(); 468 void traceRoots(gc::AutoGCSession& session, gc::TenuringTracer& mover); 469 470 size_t doPretenuring(JSRuntime* rt, JS::GCReason reason, 471 bool validPromotionRate, double promotionRate); 472 473 // Handle relocation of slots/elements pointers stored in Ion frames. 474 inline void setForwardingPointer(void* oldData, void* newData, bool direct); 475 476 inline void setDirectForwardingPointer(void* oldData, void* newData); 477 void setIndirectForwardingPointer(void* oldData, void* newData); 478 479 inline void setSlotsForwardingPointer(HeapSlot* oldSlots, HeapSlot* newSlots, 480 uint32_t nslots); 481 inline void setElementsForwardingPointer(ObjectElements* oldHeader, 482 ObjectElements* newHeader, 483 uint32_t capacity); 484 485 #ifdef DEBUG 486 bool checkForwardingPointerInsideNursery(void* ptr); 487 #endif 488 489 // Discard pointers to objects that have been freed. 490 void sweep(); 491 492 // In a minor GC, resets the start and end positions, the current chunk and 493 // current position. 494 void setNewExtentAndPosition(); 495 496 // the nursery on debug & nightly builds. 497 void clear(); 498 499 void clearMapAndSetNurseryIterators(); 500 void sweepMapAndSetObjects(); 501 502 void traceWeakMaps(gc::TenuringTracer& trc); 503 void sweepWeakMaps(); 504 505 void sweepStringsWithBuffer(); 506 507 void sweepBuffers(); 508 509 // Get per-space size limits. 510 size_t maxSpaceSize() const; 511 size_t minSpaceSize() const; 512 513 // Change the allocable space provided by the nursery. 514 void maybeResizeNursery(JS::GCOptions options, JS::GCReason reason); 515 size_t targetSize(JS::GCOptions options, JS::GCReason reason); 516 void clearRecentGrowthData(); 517 void growAllocableSpace(size_t newCapacity); 518 void shrinkAllocableSpace(size_t newCapacity); 519 void minimizeAllocableSpace(); 520 521 // Free the chunks starting at firstFreeChunk until the end of the chunks 522 // vector. Shrinks the vector but does not update maxChunkCount(). 523 void freeChunksFrom(Space& space, unsigned firstFreeChunk); 524 525 // During a semispace nursery collection, return whether a cell in fromspace 526 // was in the tospace of the previous collection, meaning that it should be 527 // tenured in this collection. 528 inline bool shouldTenure(gc::Cell* cell); 529 530 void sendTelemetry(JS::GCReason reason, mozilla::TimeDuration totalTime, 531 bool wasEmpty, double promotionRate, 532 size_t sitesPretenured); 533 534 void printCollectionProfile(JS::GCReason reason, double promotionRate); 535 void printDeduplicationData(js::StringStats& prev, js::StringStats& curr); 536 537 // Profile recording and printing. 538 void maybeClearProfileDurations(); 539 void startProfile(ProfileKey key); 540 void endProfile(ProfileKey key); 541 static void printProfileDurations(const ProfileDurations& times, 542 Sprinter& sprinter); 543 544 mozilla::TimeStamp collectionStartTime() const; 545 546 private: 547 using BufferRelocationOverlay = void*; 548 using BufferSet = HashSet<void*, PointerHasher<void*>, SystemAllocPolicy>; 549 550 struct Space { 551 // Fields used during allocation fast path go first: 552 553 // Pointer to the first unallocated byte in the nursery. 554 uintptr_t position_ = 0; 555 556 // Pointer to the last byte of space in the current chunk. 557 uintptr_t currentEnd_ = 0; 558 559 // Vector of allocated chunks to allocate from. 560 Vector<NurseryChunk*, 0, SystemAllocPolicy> chunks_; 561 562 // The index of the chunk that is currently being allocated from. 563 uint32_t currentChunk_ = 0; 564 565 // The maximum number of chunks to allocate based on capacity_. 566 uint32_t maxChunkCount_ = 0; 567 568 // These fields refer to the beginning of the nursery. They're normally 0 569 // and chunk(0).start() respectively. Except when a generational GC zeal 570 // mode is active, then they may be arbitrary (see Nursery::clear()). 571 uint32_t startChunk_ = 0; 572 uintptr_t startPosition_ = 0; 573 574 // The set of malloc-allocated buffers owned by nursery objects. Any 575 // buffers that do not belong to a promoted thing at the end of a minor GC 576 // must be freed. 577 BufferSet mallocedBuffers; 578 size_t mallocedBufferBytes = 0; 579 580 gc::ChunkKind kind; 581 582 explicit Space(gc::ChunkKind kind); 583 584 inline bool isEmpty() const; 585 inline bool isInside(const void* p) const; 586 587 // Return the logical offset within the nursery of an address in a nursery 588 // chunk (chunks are discontiguous in memory). 589 inline size_t offsetFromAddress(uintptr_t addr) const; 590 inline size_t offsetFromExclusiveAddress(uintptr_t addr) const; 591 592 void setKind(gc::ChunkKind newKind); 593 594 void clear(Nursery* nursery); 595 void moveToStartOfChunk(Nursery* nursery, unsigned chunkno); 596 void setCurrentEnd(Nursery* nursery); 597 void setStartToCurrentPosition(); 598 bool commitSubChunkRegion(size_t oldCapacity, size_t newCapacity); 599 void decommitSubChunkRegion(Nursery* nursery, size_t oldCapacity, 600 size_t newCapacity); 601 602 #ifdef DEBUG 603 void checkKind(gc::ChunkKind expected) const; 604 size_t findChunkIndex(uintptr_t chunkAddr) const; 605 #endif 606 }; 607 608 Space toSpace; 609 Space fromSpace; 610 611 gc::GCRuntime* const gc; 612 613 // The current nursery capacity measured in bytes. It may grow up to this 614 // value without a collection, allocating chunks on demand. This limit may be 615 // changed by maybeResizeNursery() each collection. It includes chunk headers. 616 size_t capacity_; 617 618 uintptr_t tenureThreshold_ = 0; 619 620 gc::PretenuringNursery pretenuringNursery; 621 622 mozilla::TimeDuration timeInChunkAlloc_; 623 624 // Report minor collections taking at least this long, if enabled. 625 bool enableProfiling_ = false; 626 bool profileWorkers_ = false; 627 628 mozilla::TimeDuration profileThreshold_; 629 630 // Whether to use semispace collection. 631 bool semispaceEnabled_; 632 633 // Whether we will nursery-allocate strings. 634 bool canAllocateStrings_; 635 636 // Whether we will nursery-allocate BigInts. 637 bool canAllocateBigInts_; 638 639 // Report how many strings were deduplicated. 640 bool reportDeduplications_; 641 642 #ifdef JS_GC_ZEAL 643 // Report on the kinds of things promoted. 644 bool reportPromotion_; 645 #endif 646 647 // Whether to report information on pretenuring, and if so the allocation 648 // threshold at which to report details of each allocation site. 649 gc::AllocSiteFilter pretenuringReportFilter_; 650 651 // Whether and why a collection of this nursery has been requested. When this 652 // happens |prevPosition_| is set to the current position and |position_| set 653 // to the end of the chunk to force the next allocation to fail. 654 JS::GCReason minorGCTriggerReason_; 655 uintptr_t prevPosition_; 656 657 // Profiling data. 658 659 ProfileTimes startTimes_; 660 ProfileDurations profileDurations_; 661 ProfileDurations totalDurations_; 662 663 // Data about the previous collection. 664 struct PreviousGC { 665 JS::GCReason reason = JS::GCReason::NO_REASON; 666 size_t nurseryCapacity = 0; 667 size_t nurseryCommitted = 0; 668 size_t nurseryUsedBytes = 0; 669 size_t nurseryUsedChunkCount = 0; 670 size_t tenuredBytes = 0; 671 size_t tenuredCells = 0; 672 mozilla::TimeStamp endTime; 673 }; 674 PreviousGC previousGC; 675 676 bool hasRecentGrowthData; 677 double smoothedTargetSize; 678 679 // During a collection most hoisted slot and element buffers indicate their 680 // new location with a forwarding pointer at the base. This does not work 681 // for buffers whose length is less than pointer width, or when different 682 // buffers might overlap each other. For these, an entry in the following 683 // table is used. 684 using ForwardedBufferMap = 685 HashMap<void*, void*, PointerHasher<void*>, SystemAllocPolicy>; 686 ForwardedBufferMap forwardedBuffers; 687 688 // When we assign a unique id to cell in the nursery, that almost always 689 // means that the cell will be in a hash table, and thus, held live, 690 // automatically moving the uid from the nursery to its new home in 691 // tenured. It is possible, if rare, for an object that acquired a uid to 692 // be dead before the next collection, in which case we need to know to 693 // remove it when we sweep. 694 // 695 // Note: we store the pointers as Cell* here, resulting in an ugly cast in 696 // sweep. This is because this structure is used to help implement 697 // stable object hashing and we have to break the cycle somehow. 698 using CellsWithUniqueIdVector = JS::GCVector<gc::Cell*, 8, SystemAllocPolicy>; 699 CellsWithUniqueIdVector cellsWithUid_; 700 701 // Lists of map and set objects with iterators allocated in the nursery. Such 702 // objects need to be swept after minor GC. 703 using MapObjectVector = Vector<MapObject*, 0, SystemAllocPolicy>; 704 MapObjectVector mapsWithNurseryIterators_; 705 using SetObjectVector = Vector<SetObject*, 0, SystemAllocPolicy>; 706 SetObjectVector setsWithNurseryIterators_; 707 708 // List of strings with StringBuffers allocated in the nursery. References 709 // to the buffers are dropped after minor GC. The list stores both the JS 710 // string and the StringBuffer to simplify interaction with AtomRefs and 711 // string deduplication. 712 using StringAndBuffer = std::pair<JSLinearString*, mozilla::StringBuffer*>; 713 using StringAndBufferVector = 714 JS::GCVector<StringAndBuffer, 8, SystemAllocPolicy>; 715 StringAndBufferVector stringBuffers_; 716 717 // Like stringBuffers_, but for extensible strings for flattened ropes. This 718 // requires a HashMap instead of a Vector because we need to remove the entry 719 // when transferring the buffer to a new extensible string during flattening. 720 using ExtensibleStringBuffers = 721 HashMap<JSLinearString*, mozilla::StringBuffer*, 722 js::PointerHasher<JSLinearString*>, js::SystemAllocPolicy>; 723 ExtensibleStringBuffers extensibleStringBuffers_; 724 725 // List of StringBuffers to release off-thread. 726 using StringBufferVector = 727 Vector<mozilla::StringBuffer*, 8, SystemAllocPolicy>; 728 StringBufferVector stringBuffersToReleaseAfterMinorGC_; 729 730 UniquePtr<NurserySweepTask> sweepTask; 731 732 // Lists of weakmaps with nursery allocated keys or values. Such objects need 733 // to be swept after minor GC. 734 using WeakMapVector = Vector<WeakMapBase*, 0, SystemAllocPolicy>; 735 WeakMapVector weakMapsWithNurseryEntries_; 736 737 UniquePtr<NurseryDecommitTask> decommitTask; 738 739 // Whether the previous collection tenured everything. This may be false if 740 // semispace is in use. 741 bool tenuredEverything; 742 743 friend class gc::GCRuntime; 744 friend class gc::TenuringTracer; 745 friend struct NurseryChunk; 746 }; 747 748 MOZ_ALWAYS_INLINE bool Nursery::isInside(const void* p) const { 749 // TODO: Split this into separate methods. 750 // TODO: Do we ever need to check both? 751 return toSpace.isInside(p) || fromSpace.isInside(p); 752 } 753 754 MOZ_ALWAYS_INLINE bool Nursery::Space::isInside(const void* p) const { 755 for (auto* chunk : chunks_) { 756 if (uintptr_t(p) - uintptr_t(chunk) < gc::ChunkSize) { 757 return true; 758 } 759 } 760 return false; 761 } 762 763 } // namespace js 764 765 #endif // gc_Nursery_h