/* * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) * Copyright (C) 2001 Peter Kelly (pmk@post.com) * Copyright (C) 2003-2017 Apple Inc. All rights reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ #pragma once #include "IterationStatus.h" #include "LargeAllocation.h" #include "MarkedAllocator.h" #include "MarkedBlock.h" #include "MarkedBlockSet.h" #include #include #include #include #include #include #include namespace JSC { class Heap; class HeapIterationScope; class LLIntOffsetsExtractor; class Subspace; class WeakSet; typedef uint32_t HeapVersion; class MarkedSpace { WTF_MAKE_NONCOPYABLE(MarkedSpace); public: // sizeStep is really a synonym for atomSize; it's no accident that they are the same. static const size_t sizeStep = MarkedBlock::atomSize; // Sizes up to this amount get a size class for each size step. static const size_t preciseCutoff = 80; // The amount of available payload in a block is the block's size minus the header. But the // header size might not be atom size aligned, so we round down the result accordingly. static const size_t blockPayload = (MarkedBlock::blockSize - sizeof(MarkedBlock)) & ~(MarkedBlock::atomSize - 1); // The largest cell we're willing to allocate in a MarkedBlock the "normal way" (i.e. using size // classes, rather than a large allocation) is half the size of the payload, rounded down. This // ensures that we only use the size class approach if it means being able to pack two things // into one block. static const size_t largeCutoff = (blockPayload / 2) & ~(sizeStep - 1); static const size_t numSizeClasses = largeCutoff / sizeStep; static const HeapVersion nullVersion = 0; // The version of freshly allocated blocks. static const HeapVersion initialVersion = 2; // The version that the heap starts out with. Set to make sure that nextVersion(nullVersion) != initialVersion. static HeapVersion nextVersion(HeapVersion version) { version++; if (version == nullVersion) version = initialVersion; return version; } static size_t sizeClassToIndex(size_t size) { ASSERT(size); return (size + sizeStep - 1) / sizeStep - 1; } static size_t indexToSizeClass(size_t index) { return (index + 1) * sizeStep; } MarkedSpace(Heap*); ~MarkedSpace(); Heap* heap() const { return m_heap; } void lastChanceToFinalize(); // You must call stopAllocating before you call this. static size_t optimalSizeFor(size_t); void prepareForAllocation(); void visitWeakSets(SlotVisitor&); void reapWeakSets(); MarkedBlockSet& blocks() { return m_blocks; } void willStartIterating(); bool isIterating() const { return m_isIterating; } void didFinishIterating(); void stopAllocating(); void resumeAllocating(); // If we just stopped allocation but we didn't do a collection, we need to resume allocation. void prepareForMarking(); void prepareForConservativeScan(); typedef HashSet::iterator BlockIterator; template void forEachLiveCell(HeapIterationScope&, const Functor&); template void forEachDeadCell(HeapIterationScope&, const Functor&); template void forEachBlock(const Functor&); void shrink(); void freeBlock(MarkedBlock::Handle*); void freeOrShrinkBlock(MarkedBlock::Handle*); void didAddBlock(MarkedBlock::Handle*); void didConsumeFreeList(MarkedBlock::Handle*); void didAllocateInBlock(MarkedBlock::Handle*); void beginMarking(); void endMarking(); void snapshotUnswept(); void clearNewlyAllocated(); void sweep(); void sweepLargeAllocations(); void assertNoUnswept(); size_t objectCount(); size_t size(); size_t capacity(); bool isPagedOut(double deadline); HeapVersion markingVersion() const { return m_markingVersion; } HeapVersion newlyAllocatedVersion() const { return m_newlyAllocatedVersion; } const Vector& largeAllocations() const { return m_largeAllocations; } unsigned largeAllocationsNurseryOffset() const { return m_largeAllocationsNurseryOffset; } unsigned largeAllocationsOffsetForThisCollection() const { return m_largeAllocationsOffsetForThisCollection; } // These are cached pointers and offsets for quickly searching the large allocations that are // relevant to this collection. LargeAllocation** largeAllocationsForThisCollectionBegin() const { return m_largeAllocationsForThisCollectionBegin; } LargeAllocation** largeAllocationsForThisCollectionEnd() const { return m_largeAllocationsForThisCollectionEnd; } unsigned largeAllocationsForThisCollectionSize() const { return m_largeAllocationsForThisCollectionSize; } MarkedAllocator* firstAllocator() const { return m_firstAllocator; } MarkedAllocator* allocatorForEmptyAllocation() const { return m_allocatorForEmptyAllocation; } MarkedBlock::Handle* findEmptyBlockToSteal(); Lock& allocatorLock() { return m_allocatorLock; } MarkedAllocator* addMarkedAllocator(const AbstractLocker&, Subspace*, size_t cellSize); // When this is true it means that we have flipped but the mark bits haven't converged yet. bool isMarking() const { return m_isMarking; } void dumpBits(PrintStream& = WTF::dataFile()); JS_EXPORT_PRIVATE static std::array s_sizeClassForSizeStep; private: friend class LLIntOffsetsExtractor; friend class JIT; friend class WeakSet; friend class Subspace; void* allocateSlow(Subspace&, GCDeferralContext*, size_t); void* tryAllocateSlow(Subspace&, GCDeferralContext*, size_t); static void initializeSizeClassForStepSize(); void initializeSubspace(Subspace&); template inline void forEachAllocator(const Functor&); void addActiveWeakSet(WeakSet*); Vector m_subspaces; Vector m_largeAllocations; unsigned m_largeAllocationsNurseryOffset { 0 }; unsigned m_largeAllocationsOffsetForThisCollection { 0 }; unsigned m_largeAllocationsNurseryOffsetForSweep { 0 }; LargeAllocation** m_largeAllocationsForThisCollectionBegin { nullptr }; LargeAllocation** m_largeAllocationsForThisCollectionEnd { nullptr }; unsigned m_largeAllocationsForThisCollectionSize { 0 }; Heap* m_heap; HeapVersion m_markingVersion { initialVersion }; HeapVersion m_newlyAllocatedVersion { initialVersion }; size_t m_capacity; bool m_isIterating; bool m_isMarking { false }; MarkedBlockSet m_blocks; SentinelLinkedList> m_activeWeakSets; SentinelLinkedList> m_newActiveWeakSets; Lock m_allocatorLock; Bag m_bagOfAllocators; MarkedAllocator* m_firstAllocator { nullptr }; MarkedAllocator* m_lastAllocator { nullptr }; MarkedAllocator* m_allocatorForEmptyAllocation { nullptr }; }; template inline void MarkedSpace::forEachBlock(const Functor& functor) { forEachAllocator( [&] (MarkedAllocator& allocator) -> IterationStatus { allocator.forEachBlock(functor); return IterationStatus::Continue; }); } template void MarkedSpace::forEachAllocator(const Functor& functor) { for (MarkedAllocator* allocator = m_firstAllocator; allocator; allocator = allocator->nextAllocator()) { if (functor(*allocator) == IterationStatus::Done) return; } } ALWAYS_INLINE size_t MarkedSpace::optimalSizeFor(size_t bytes) { ASSERT(bytes); if (bytes <= preciseCutoff) return WTF::roundUpToMultipleOf(bytes); if (bytes <= largeCutoff) return s_sizeClassForSizeStep[sizeClassToIndex(bytes)]; return bytes; } } // namespace JSC