| Index: Source/platform/heap/HeapPage.cpp
|
| diff --git a/Source/platform/heap/Heap.cpp b/Source/platform/heap/HeapPage.cpp
|
| similarity index 72%
|
| copy from Source/platform/heap/Heap.cpp
|
| copy to Source/platform/heap/HeapPage.cpp
|
| index d4a3a5dbbfdaa7b2b8d0e5cb974342bf94cf1c9f..9b6e4c3a7054f2d5f210df382211dfc3b5d8b027 100644
|
| --- a/Source/platform/heap/Heap.cpp
|
| +++ b/Source/platform/heap/HeapPage.cpp
|
| @@ -29,13 +29,14 @@
|
| */
|
|
|
| #include "config.h"
|
| -#include "platform/heap/Heap.h"
|
| +#include "platform/heap/HeapPage.h"
|
|
|
| #include "platform/ScriptForbiddenScope.h"
|
| #include "platform/Task.h"
|
| #include "platform/TraceEvent.h"
|
| #include "platform/heap/BlinkGCMemoryDumpProvider.h"
|
| #include "platform/heap/CallbackStack.h"
|
| +#include "platform/heap/Heap.h"
|
| #include "platform/heap/MarkingVisitor.h"
|
| #include "platform/heap/PageMemory.h"
|
| #include "platform/heap/PagePool.h"
|
| @@ -61,13 +62,6 @@
|
| #include <utility>
|
| #endif
|
|
|
| -#if OS(POSIX)
|
| -#include <sys/mman.h>
|
| -#include <unistd.h>
|
| -#elif OS(WIN)
|
| -#include <windows.h>
|
| -#endif
|
| -
|
| #ifdef ANNOTATE_CONTIGUOUS_CONTAINER
|
| // FIXME: have ContainerAnnotations.h define an ENABLE_-style name instead.
|
| #define ENABLE_ASAN_CONTAINER_ANNOTATIONS 1
|
| @@ -112,91 +106,6 @@ static String classOf(const void* object)
|
| }
|
| #endif
|
|
|
| -class GCForbiddenScope final {
|
| -public:
|
| - explicit GCForbiddenScope(ThreadState* state)
|
| - : m_state(state)
|
| - {
|
| - // Prevent nested collectGarbage() invocations.
|
| - m_state->enterGCForbiddenScope();
|
| - }
|
| -
|
| - ~GCForbiddenScope()
|
| - {
|
| - m_state->leaveGCForbiddenScope();
|
| - }
|
| -
|
| -private:
|
| - ThreadState* m_state;
|
| -};
|
| -
|
| -class GCScope final {
|
| -public:
|
| - GCScope(ThreadState* state, ThreadState::StackState stackState, ThreadState::GCType gcType)
|
| - : m_state(state)
|
| - , m_gcForbiddenScope(state)
|
| - // See collectGarbageForTerminatingThread() comment on why a
|
| - // safepoint scope isn't entered for its GCScope.
|
| - , m_safePointScope(stackState, gcType != ThreadState::ThreadTerminationGC ? state : nullptr)
|
| - , m_gcType(gcType)
|
| - , m_parkedAllThreads(false)
|
| - {
|
| - TRACE_EVENT0("blink_gc", "Heap::GCScope");
|
| - const char* samplingState = TRACE_EVENT_GET_SAMPLING_STATE();
|
| - if (m_state->isMainThread())
|
| - TRACE_EVENT_SET_SAMPLING_STATE("blink_gc", "BlinkGCWaiting");
|
| -
|
| - ASSERT(m_state->checkThread());
|
| -
|
| - // TODO(haraken): In an unlikely coincidence that two threads decide
|
| - // to collect garbage at the same time, avoid doing two GCs in
|
| - // a row.
|
| - if (LIKELY(gcType != ThreadState::ThreadTerminationGC && ThreadState::stopThreads()))
|
| - m_parkedAllThreads = true;
|
| -
|
| - switch (gcType) {
|
| - case ThreadState::GCWithSweep:
|
| - case ThreadState::GCWithoutSweep:
|
| - m_visitor = adoptPtr(new MarkingVisitor<Visitor::GlobalMarking>());
|
| - break;
|
| - case ThreadState::TakeSnapshot:
|
| - m_visitor = adoptPtr(new MarkingVisitor<Visitor::SnapshotMarking>());
|
| - break;
|
| - case ThreadState::ThreadTerminationGC:
|
| - m_visitor = adoptPtr(new MarkingVisitor<Visitor::ThreadLocalMarking>());
|
| - break;
|
| - default:
|
| - ASSERT_NOT_REACHED();
|
| - }
|
| -
|
| - if (m_state->isMainThread())
|
| - TRACE_EVENT_SET_NONCONST_SAMPLING_STATE(samplingState);
|
| - }
|
| -
|
| - bool allThreadsParked() const { return m_parkedAllThreads; }
|
| - Visitor* visitor() const { return m_visitor.get(); }
|
| -
|
| - ~GCScope()
|
| - {
|
| - // Only cleanup if we parked all threads in which case the GC happened
|
| - // and we need to resume the other threads.
|
| - if (LIKELY(m_gcType != ThreadState::ThreadTerminationGC && m_parkedAllThreads))
|
| - ThreadState::resumeThreads();
|
| - }
|
| -
|
| -private:
|
| - ThreadState* m_state;
|
| - // The ordering of the two scope objects matters: GCs must first be forbidden
|
| - // before entering the safe point scope. Prior to reaching the safe point,
|
| - // ThreadState::runScheduledGC() is called. See its comment why we need
|
| - // to be in a GC forbidden scope when doing so.
|
| - GCForbiddenScope m_gcForbiddenScope;
|
| - SafePointScope m_safePointScope;
|
| - ThreadState::GCType m_gcType;
|
| - OwnPtr<Visitor> m_visitor;
|
| - bool m_parkedAllThreads; // False if we fail to park all threads
|
| -};
|
| -
|
| #if ENABLE(ASSERT)
|
| NO_SANITIZE_ADDRESS
|
| void HeapObjectHeader::zapMagic()
|
| @@ -1915,644 +1824,4 @@ void HeapDoesNotContainCache::addEntry(Address address)
|
| m_entries[index] = cachePage;
|
| }
|
|
|
| -void Heap::flushHeapDoesNotContainCache()
|
| -{
|
| - s_heapDoesNotContainCache->flush();
|
| -}
|
| -
|
| -void Heap::init()
|
| -{
|
| - ThreadState::init();
|
| - s_markingStack = new CallbackStack();
|
| - s_postMarkingCallbackStack = new CallbackStack();
|
| - s_globalWeakCallbackStack = new CallbackStack();
|
| - s_ephemeronStack = new CallbackStack();
|
| - s_heapDoesNotContainCache = new HeapDoesNotContainCache();
|
| - s_freePagePool = new FreePagePool();
|
| - s_orphanedPagePool = new OrphanedPagePool();
|
| - s_allocatedSpace = 0;
|
| - s_allocatedObjectSize = 0;
|
| - s_objectSizeAtLastGC = 0;
|
| - s_markedObjectSize = 0;
|
| - s_markedObjectSizeAtLastCompleteSweep = 0;
|
| - s_persistentCount = 0;
|
| - s_persistentCountAtLastGC = 0;
|
| - s_collectedPersistentCount = 0;
|
| - s_partitionAllocSizeAtLastGC = WTF::Partitions::totalSizeOfCommittedPages();
|
| - s_estimatedMarkingTimePerByte = 0.0;
|
| -
|
| - GCInfoTable::init();
|
| -
|
| - if (Platform::current() && Platform::current()->currentThread())
|
| - Platform::current()->registerMemoryDumpProvider(BlinkGCMemoryDumpProvider::instance());
|
| -}
|
| -
|
| -void Heap::shutdown()
|
| -{
|
| - if (Platform::current() && Platform::current()->currentThread())
|
| - Platform::current()->unregisterMemoryDumpProvider(BlinkGCMemoryDumpProvider::instance());
|
| - s_shutdownCalled = true;
|
| - ThreadState::shutdownHeapIfNecessary();
|
| -}
|
| -
|
| -void Heap::doShutdown()
|
| -{
|
| - // We don't want to call doShutdown() twice.
|
| - if (!s_markingStack)
|
| - return;
|
| -
|
| - ASSERT(!ThreadState::attachedThreads().size());
|
| - delete s_heapDoesNotContainCache;
|
| - s_heapDoesNotContainCache = nullptr;
|
| - delete s_freePagePool;
|
| - s_freePagePool = nullptr;
|
| - delete s_orphanedPagePool;
|
| - s_orphanedPagePool = nullptr;
|
| - delete s_globalWeakCallbackStack;
|
| - s_globalWeakCallbackStack = nullptr;
|
| - delete s_postMarkingCallbackStack;
|
| - s_postMarkingCallbackStack = nullptr;
|
| - delete s_markingStack;
|
| - s_markingStack = nullptr;
|
| - delete s_ephemeronStack;
|
| - s_ephemeronStack = nullptr;
|
| - delete s_regionTree;
|
| - s_regionTree = nullptr;
|
| - GCInfoTable::shutdown();
|
| - ThreadState::shutdown();
|
| - ASSERT(Heap::allocatedSpace() == 0);
|
| -}
|
| -
|
| -#if ENABLE(ASSERT)
|
| -BasePage* Heap::findPageFromAddress(Address address)
|
| -{
|
| - MutexLocker lock(ThreadState::threadAttachMutex());
|
| - for (ThreadState* state : ThreadState::attachedThreads()) {
|
| - if (BasePage* page = state->findPageFromAddress(address))
|
| - return page;
|
| - }
|
| - return nullptr;
|
| -}
|
| -#endif
|
| -
|
| -Address Heap::checkAndMarkPointer(Visitor* visitor, Address address)
|
| -{
|
| - ASSERT(ThreadState::current()->isInGC());
|
| -
|
| -#if !ENABLE(ASSERT)
|
| - if (s_heapDoesNotContainCache->lookup(address))
|
| - return nullptr;
|
| -#endif
|
| -
|
| - if (BasePage* page = lookup(address)) {
|
| - ASSERT(page->contains(address));
|
| - ASSERT(!page->orphaned());
|
| - ASSERT(!s_heapDoesNotContainCache->lookup(address));
|
| - page->checkAndMarkPointer(visitor, address);
|
| - return address;
|
| - }
|
| -
|
| -#if !ENABLE(ASSERT)
|
| - s_heapDoesNotContainCache->addEntry(address);
|
| -#else
|
| - if (!s_heapDoesNotContainCache->lookup(address))
|
| - s_heapDoesNotContainCache->addEntry(address);
|
| -#endif
|
| - return nullptr;
|
| -}
|
| -
|
| -#if ENABLE(GC_PROFILING)
|
| -const GCInfo* Heap::findGCInfo(Address address)
|
| -{
|
| - return ThreadState::findGCInfoFromAllThreads(address);
|
| -}
|
| -#endif
|
| -
|
| -#if ENABLE(GC_PROFILING)
|
| -String Heap::createBacktraceString()
|
| -{
|
| - int framesToShow = 3;
|
| - int stackFrameSize = 16;
|
| - ASSERT(stackFrameSize >= framesToShow);
|
| - using FramePointer = void*;
|
| - FramePointer* stackFrame = static_cast<FramePointer*>(alloca(sizeof(FramePointer) * stackFrameSize));
|
| - WTFGetBacktrace(stackFrame, &stackFrameSize);
|
| -
|
| - StringBuilder builder;
|
| - builder.append("Persistent");
|
| - bool didAppendFirstName = false;
|
| - // Skip frames before/including "blink::Persistent".
|
| - bool didSeePersistent = false;
|
| - for (int i = 0; i < stackFrameSize && framesToShow > 0; ++i) {
|
| - FrameToNameScope frameToName(stackFrame[i]);
|
| - if (!frameToName.nullableName())
|
| - continue;
|
| - if (strstr(frameToName.nullableName(), "blink::Persistent")) {
|
| - didSeePersistent = true;
|
| - continue;
|
| - }
|
| - if (!didSeePersistent)
|
| - continue;
|
| - if (!didAppendFirstName) {
|
| - didAppendFirstName = true;
|
| - builder.append(" ... Backtrace:");
|
| - }
|
| - builder.append("\n\t");
|
| - builder.append(frameToName.nullableName());
|
| - --framesToShow;
|
| - }
|
| - return builder.toString().replace("blink::", "");
|
| -}
|
| -#endif
|
| -
|
| -void Heap::pushTraceCallback(void* object, TraceCallback callback)
|
| -{
|
| - ASSERT(ThreadState::current()->isInGC());
|
| -
|
| - // Trace should never reach an orphaned page.
|
| - ASSERT(!Heap::orphanedPagePool()->contains(object));
|
| - CallbackStack::Item* slot = s_markingStack->allocateEntry();
|
| - *slot = CallbackStack::Item(object, callback);
|
| -}
|
| -
|
| -bool Heap::popAndInvokeTraceCallback(Visitor* visitor)
|
| -{
|
| - CallbackStack::Item* item = s_markingStack->pop();
|
| - if (!item)
|
| - return false;
|
| - item->call(visitor);
|
| - return true;
|
| -}
|
| -
|
| -void Heap::pushPostMarkingCallback(void* object, TraceCallback callback)
|
| -{
|
| - ASSERT(ThreadState::current()->isInGC());
|
| -
|
| - // Trace should never reach an orphaned page.
|
| - ASSERT(!Heap::orphanedPagePool()->contains(object));
|
| - CallbackStack::Item* slot = s_postMarkingCallbackStack->allocateEntry();
|
| - *slot = CallbackStack::Item(object, callback);
|
| -}
|
| -
|
| -bool Heap::popAndInvokePostMarkingCallback(Visitor* visitor)
|
| -{
|
| - if (CallbackStack::Item* item = s_postMarkingCallbackStack->pop()) {
|
| - item->call(visitor);
|
| - return true;
|
| - }
|
| - return false;
|
| -}
|
| -
|
| -void Heap::pushGlobalWeakCallback(void** cell, WeakCallback callback)
|
| -{
|
| - ASSERT(ThreadState::current()->isInGC());
|
| -
|
| - // Trace should never reach an orphaned page.
|
| - ASSERT(!Heap::orphanedPagePool()->contains(cell));
|
| - CallbackStack::Item* slot = s_globalWeakCallbackStack->allocateEntry();
|
| - *slot = CallbackStack::Item(cell, callback);
|
| -}
|
| -
|
| -void Heap::pushThreadLocalWeakCallback(void* closure, void* object, WeakCallback callback)
|
| -{
|
| - ASSERT(ThreadState::current()->isInGC());
|
| -
|
| - // Trace should never reach an orphaned page.
|
| - ASSERT(!Heap::orphanedPagePool()->contains(object));
|
| - ThreadState* state = pageFromObject(object)->heap()->threadState();
|
| - state->pushThreadLocalWeakCallback(closure, callback);
|
| -}
|
| -
|
| -bool Heap::popAndInvokeGlobalWeakCallback(Visitor* visitor)
|
| -{
|
| - if (CallbackStack::Item* item = s_globalWeakCallbackStack->pop()) {
|
| - item->call(visitor);
|
| - return true;
|
| - }
|
| - return false;
|
| -}
|
| -
|
| -void Heap::registerWeakTable(void* table, EphemeronCallback iterationCallback, EphemeronCallback iterationDoneCallback)
|
| -{
|
| - ASSERT(ThreadState::current()->isInGC());
|
| -
|
| - // Trace should never reach an orphaned page.
|
| - ASSERT(!Heap::orphanedPagePool()->contains(table));
|
| - CallbackStack::Item* slot = s_ephemeronStack->allocateEntry();
|
| - *slot = CallbackStack::Item(table, iterationCallback);
|
| -
|
| - // Register a post-marking callback to tell the tables that
|
| - // ephemeron iteration is complete.
|
| - pushPostMarkingCallback(table, iterationDoneCallback);
|
| -}
|
| -
|
| -#if ENABLE(ASSERT)
|
| -bool Heap::weakTableRegistered(const void* table)
|
| -{
|
| - ASSERT(s_ephemeronStack);
|
| - return s_ephemeronStack->hasCallbackForObject(table);
|
| -}
|
| -#endif
|
| -
|
| -void Heap::preGC()
|
| -{
|
| - ASSERT(!ThreadState::current()->isInGC());
|
| - for (ThreadState* state : ThreadState::attachedThreads())
|
| - state->preGC();
|
| -}
|
| -
|
| -void Heap::postGC(ThreadState::GCType gcType)
|
| -{
|
| - ASSERT(ThreadState::current()->isInGC());
|
| - for (ThreadState* state : ThreadState::attachedThreads())
|
| - state->postGC(gcType);
|
| -}
|
| -
|
| -const char* Heap::gcReasonString(GCReason reason)
|
| -{
|
| - switch (reason) {
|
| -#define STRINGIFY_REASON(reason) case reason: return #reason;
|
| - STRINGIFY_REASON(IdleGC);
|
| - STRINGIFY_REASON(PreciseGC);
|
| - STRINGIFY_REASON(ConservativeGC);
|
| - STRINGIFY_REASON(ForcedGC);
|
| -#undef STRINGIFY_REASON
|
| - case NumberOfGCReason: ASSERT_NOT_REACHED();
|
| - }
|
| - return "<Unknown>";
|
| -}
|
| -
|
| -void Heap::collectGarbage(ThreadState::StackState stackState, ThreadState::GCType gcType, GCReason reason)
|
| -{
|
| - ThreadState* state = ThreadState::current();
|
| - // Nested collectGarbage() invocations aren't supported.
|
| - RELEASE_ASSERT(!state->isGCForbidden());
|
| - state->completeSweep();
|
| -
|
| - GCScope gcScope(state, stackState, gcType);
|
| - // Check if we successfully parked the other threads. If not we bail out of
|
| - // the GC.
|
| - if (!gcScope.allThreadsParked())
|
| - return;
|
| -
|
| - if (state->isMainThread())
|
| - ScriptForbiddenScope::enter();
|
| -
|
| - TRACE_EVENT2("blink_gc", "Heap::collectGarbage",
|
| - "lazySweeping", gcType == ThreadState::GCWithoutSweep,
|
| - "gcReason", gcReasonString(reason));
|
| - TRACE_EVENT_SCOPED_SAMPLING_STATE("blink_gc", "BlinkGC");
|
| - double timeStamp = WTF::currentTimeMS();
|
| -
|
| - if (gcType == ThreadState::TakeSnapshot)
|
| - BlinkGCMemoryDumpProvider::instance()->clearProcessDumpForCurrentGC();
|
| -
|
| - // Disallow allocation during garbage collection (but not during the
|
| - // finalization that happens when the gcScope is torn down).
|
| - ThreadState::NoAllocationScope noAllocationScope(state);
|
| -
|
| - preGC();
|
| -
|
| - StackFrameDepthScope stackDepthScope;
|
| -
|
| - size_t totalObjectSize = Heap::allocatedObjectSize() + Heap::markedObjectSize();
|
| - if (gcType != ThreadState::TakeSnapshot)
|
| - Heap::resetHeapCounters();
|
| -
|
| - // 1. Trace persistent roots.
|
| - ThreadState::visitPersistentRoots(gcScope.visitor());
|
| -
|
| - // 2. Trace objects reachable from the stack. We do this independent of the
|
| - // given stackState since other threads might have a different stack state.
|
| - ThreadState::visitStackRoots(gcScope.visitor());
|
| -
|
| - // 3. Transitive closure to trace objects including ephemerons.
|
| - processMarkingStack(gcScope.visitor());
|
| -
|
| - postMarkingProcessing(gcScope.visitor());
|
| - globalWeakProcessing(gcScope.visitor());
|
| -
|
| - // Now we can delete all orphaned pages because there are no dangling
|
| - // pointers to the orphaned pages. (If we have such dangling pointers,
|
| - // we should have crashed during marking before getting here.)
|
| - orphanedPagePool()->decommitOrphanedPages();
|
| -
|
| - double markingTimeInMilliseconds = WTF::currentTimeMS() - timeStamp;
|
| - s_estimatedMarkingTimePerByte = totalObjectSize ? (markingTimeInMilliseconds / 1000 / totalObjectSize) : 0;
|
| -
|
| - Platform::current()->histogramCustomCounts("BlinkGC.CollectGarbage", markingTimeInMilliseconds, 0, 10 * 1000, 50);
|
| - Platform::current()->histogramCustomCounts("BlinkGC.TotalObjectSpace", Heap::allocatedObjectSize() / 1024, 0, 4 * 1024 * 1024, 50);
|
| - Platform::current()->histogramCustomCounts("BlinkGC.TotalAllocatedSpace", Heap::allocatedSpace() / 1024, 0, 4 * 1024 * 1024, 50);
|
| - Platform::current()->histogramEnumeration("BlinkGC.GCReason", reason, NumberOfGCReason);
|
| - Heap::reportMemoryUsageHistogram();
|
| - WTF::Partitions::reportMemoryUsageHistogram();
|
| -
|
| - postGC(gcType);
|
| -
|
| - if (state->isMainThread())
|
| - ScriptForbiddenScope::exit();
|
| -}
|
| -
|
| -void Heap::collectGarbageForTerminatingThread(ThreadState* state)
|
| -{
|
| - {
|
| - // A thread-specific termination GC must not allow other global GCs to go
|
| - // ahead while it is running, hence the termination GC does not enter a
|
| - // safepoint. GCScope will not enter also a safepoint scope for
|
| - // ThreadTerminationGC.
|
| - GCScope gcScope(state, ThreadState::NoHeapPointersOnStack, ThreadState::ThreadTerminationGC);
|
| -
|
| - ThreadState::NoAllocationScope noAllocationScope(state);
|
| -
|
| - state->preGC();
|
| - StackFrameDepthScope stackDepthScope;
|
| -
|
| - // 1. Trace the thread local persistent roots. For thread local GCs we
|
| - // don't trace the stack (ie. no conservative scanning) since this is
|
| - // only called during thread shutdown where there should be no objects
|
| - // on the stack.
|
| - // We also assume that orphaned pages have no objects reachable from
|
| - // persistent handles on other threads or CrossThreadPersistents. The
|
| - // only cases where this could happen is if a subsequent conservative
|
| - // global GC finds a "pointer" on the stack or due to a programming
|
| - // error where an object has a dangling cross-thread pointer to an
|
| - // object on this heap.
|
| - state->visitPersistents(gcScope.visitor());
|
| -
|
| - // 2. Trace objects reachable from the thread's persistent roots
|
| - // including ephemerons.
|
| - processMarkingStack(gcScope.visitor());
|
| -
|
| - postMarkingProcessing(gcScope.visitor());
|
| - globalWeakProcessing(gcScope.visitor());
|
| -
|
| - state->postGC(ThreadState::GCWithSweep);
|
| - }
|
| - state->preSweep();
|
| -}
|
| -
|
| -void Heap::processMarkingStack(Visitor* visitor)
|
| -{
|
| - // Ephemeron fixed point loop.
|
| - do {
|
| - {
|
| - // Iteratively mark all objects that are reachable from the objects
|
| - // currently pushed onto the marking stack.
|
| - TRACE_EVENT0("blink_gc", "Heap::processMarkingStackSingleThreaded");
|
| - while (popAndInvokeTraceCallback(visitor)) { }
|
| - }
|
| -
|
| - {
|
| - // Mark any strong pointers that have now become reachable in
|
| - // ephemeron maps.
|
| - TRACE_EVENT0("blink_gc", "Heap::processEphemeronStack");
|
| - s_ephemeronStack->invokeEphemeronCallbacks(visitor);
|
| - }
|
| -
|
| - // Rerun loop if ephemeron processing queued more objects for tracing.
|
| - } while (!s_markingStack->isEmpty());
|
| -}
|
| -
|
| -void Heap::postMarkingProcessing(Visitor* visitor)
|
| -{
|
| - TRACE_EVENT0("blink_gc", "Heap::postMarkingProcessing");
|
| - // Call post-marking callbacks including:
|
| - // 1. the ephemeronIterationDone callbacks on weak tables to do cleanup
|
| - // (specifically to clear the queued bits for weak hash tables), and
|
| - // 2. the markNoTracing callbacks on collection backings to mark them
|
| - // if they are only reachable from their front objects.
|
| - while (popAndInvokePostMarkingCallback(visitor)) { }
|
| -
|
| - s_ephemeronStack->clear();
|
| -
|
| - // Post-marking callbacks should not trace any objects and
|
| - // therefore the marking stack should be empty after the
|
| - // post-marking callbacks.
|
| - ASSERT(s_markingStack->isEmpty());
|
| -}
|
| -
|
| -void Heap::globalWeakProcessing(Visitor* visitor)
|
| -{
|
| - TRACE_EVENT0("blink_gc", "Heap::globalWeakProcessing");
|
| - // Call weak callbacks on objects that may now be pointing to dead objects.
|
| - while (popAndInvokeGlobalWeakCallback(visitor)) { }
|
| -
|
| - // It is not permitted to trace pointers of live objects in the weak
|
| - // callback phase, so the marking stack should still be empty here.
|
| - ASSERT(s_markingStack->isEmpty());
|
| -}
|
| -
|
| -void Heap::collectAllGarbage()
|
| -{
|
| - // FIXME: Oilpan: we should perform a single GC and everything
|
| - // should die. Unfortunately it is not the case for all objects
|
| - // because the hierarchy was not completely moved to the heap and
|
| - // some heap allocated objects own objects that contain persistents
|
| - // pointing to other heap allocated objects.
|
| - size_t previousLiveObjects = 0;
|
| - for (int i = 0; i < 5; ++i) {
|
| - collectGarbage(ThreadState::NoHeapPointersOnStack, ThreadState::GCWithSweep, ForcedGC);
|
| - size_t liveObjects = Heap::markedObjectSize();
|
| - if (liveObjects == previousLiveObjects)
|
| - break;
|
| - previousLiveObjects = liveObjects;
|
| - }
|
| -}
|
| -
|
| -double Heap::estimatedMarkingTime()
|
| -{
|
| - ASSERT(ThreadState::current()->isMainThread());
|
| -
|
| - // Use 8 ms as initial estimated marking time.
|
| - // 8 ms is long enough for low-end mobile devices to mark common
|
| - // real-world object graphs.
|
| - if (s_estimatedMarkingTimePerByte == 0)
|
| - return 0.008;
|
| -
|
| - // Assuming that the collection rate of this GC will be mostly equal to
|
| - // the collection rate of the last GC, estimate the marking time of this GC.
|
| - return s_estimatedMarkingTimePerByte * (Heap::allocatedObjectSize() + Heap::markedObjectSize());
|
| -}
|
| -
|
| -void Heap::reportMemoryUsageHistogram()
|
| -{
|
| - static size_t supportedMaxSizeInMB = 4 * 1024;
|
| - static size_t observedMaxSizeInMB = 0;
|
| -
|
| - // We only report the memory in the main thread.
|
| - if (!isMainThread())
|
| - return;
|
| - // +1 is for rounding up the sizeInMB.
|
| - size_t sizeInMB = Heap::allocatedSpace() / 1024 / 1024 + 1;
|
| - if (sizeInMB >= supportedMaxSizeInMB)
|
| - sizeInMB = supportedMaxSizeInMB - 1;
|
| - if (sizeInMB > observedMaxSizeInMB) {
|
| - // Send a UseCounter only when we see the highest memory usage
|
| - // we've ever seen.
|
| - Platform::current()->histogramEnumeration("BlinkGC.CommittedSize", sizeInMB, supportedMaxSizeInMB);
|
| - observedMaxSizeInMB = sizeInMB;
|
| - }
|
| -}
|
| -
|
| -void Heap::reportMemoryUsageForTracing()
|
| -{
|
| - bool gcTracingEnabled;
|
| - TRACE_EVENT_CATEGORY_GROUP_ENABLED("blink_gc", &gcTracingEnabled);
|
| - if (!gcTracingEnabled)
|
| - return;
|
| -
|
| - // These values are divided by 1024 to avoid overflow in practical cases (TRACE_COUNTER values are 32-bit ints).
|
| - // They are capped to INT_MAX just in case.
|
| - TRACE_COUNTER1("blink_gc", "Heap::allocatedObjectSizeKB", std::min(Heap::allocatedObjectSize() / 1024, static_cast<size_t>(INT_MAX)));
|
| - TRACE_COUNTER1("blink_gc", "Heap::markedObjectSizeKB", std::min(Heap::markedObjectSize() / 1024, static_cast<size_t>(INT_MAX)));
|
| - TRACE_COUNTER1("blink_gc", "Heap::markedObjectSizeAtLastCompleteSweepKB", std::min(Heap::markedObjectSizeAtLastCompleteSweep() / 1024, static_cast<size_t>(INT_MAX)));
|
| - TRACE_COUNTER1("blink_gc", "Heap::allocatedSpaceKB", std::min(Heap::allocatedSpace() / 1024, static_cast<size_t>(INT_MAX)));
|
| - TRACE_COUNTER1("blink_gc", "Heap::objectSizeAtLastGCKB", std::min(Heap::objectSizeAtLastGC() / 1024, static_cast<size_t>(INT_MAX)));
|
| - TRACE_COUNTER1("blink_gc", "Heap::persistentCount", std::min(Heap::persistentCount(), static_cast<size_t>(INT_MAX)));
|
| - TRACE_COUNTER1("blink_gc", "Heap::persistentCountAtLastGC", std::min(Heap::persistentCountAtLastGC(), static_cast<size_t>(INT_MAX)));
|
| - TRACE_COUNTER1("blink_gc", "Heap::collectedPersistentCount", std::min(Heap::collectedPersistentCount(), static_cast<size_t>(INT_MAX)));
|
| - TRACE_COUNTER1("blink_gc", "Heap::partitionAllocSizeAtLastGCKB", std::min(Heap::partitionAllocSizeAtLastGC() / 1024, static_cast<size_t>(INT_MAX)));
|
| - TRACE_COUNTER1("blink_gc", "Partitions::totalSizeOfCommittedPagesKB", std::min(WTF::Partitions::totalSizeOfCommittedPages() / 1024, static_cast<size_t>(INT_MAX)));
|
| -}
|
| -
|
| -size_t Heap::objectPayloadSizeForTesting()
|
| -{
|
| - size_t objectPayloadSize = 0;
|
| - for (ThreadState* state : ThreadState::attachedThreads()) {
|
| - state->setGCState(ThreadState::GCRunning);
|
| - state->makeConsistentForGC();
|
| - objectPayloadSize += state->objectPayloadSizeForTesting();
|
| - state->setGCState(ThreadState::EagerSweepScheduled);
|
| - state->setGCState(ThreadState::Sweeping);
|
| - state->setGCState(ThreadState::NoGCScheduled);
|
| - }
|
| - return objectPayloadSize;
|
| -}
|
| -
|
| -BasePage* Heap::lookup(Address address)
|
| -{
|
| - ASSERT(ThreadState::current()->isInGC());
|
| - if (!s_regionTree)
|
| - return nullptr;
|
| - if (PageMemoryRegion* region = s_regionTree->lookup(address)) {
|
| - BasePage* page = region->pageFromAddress(address);
|
| - return page && !page->orphaned() ? page : nullptr;
|
| - }
|
| - return nullptr;
|
| -}
|
| -
|
| -static Mutex& regionTreeMutex()
|
| -{
|
| - AtomicallyInitializedStaticReference(Mutex, mutex, new Mutex);
|
| - return mutex;
|
| -}
|
| -
|
| -void Heap::removePageMemoryRegion(PageMemoryRegion* region)
|
| -{
|
| - // Deletion of large objects (and thus their regions) can happen
|
| - // concurrently on sweeper threads. Removal can also happen during thread
|
| - // shutdown, but that case is safe. Regardless, we make all removals
|
| - // mutually exclusive.
|
| - MutexLocker locker(regionTreeMutex());
|
| - RegionTree::remove(region, &s_regionTree);
|
| -}
|
| -
|
| -void Heap::addPageMemoryRegion(PageMemoryRegion* region)
|
| -{
|
| - MutexLocker locker(regionTreeMutex());
|
| - RegionTree::add(new RegionTree(region), &s_regionTree);
|
| -}
|
| -
|
| -PageMemoryRegion* Heap::RegionTree::lookup(Address address)
|
| -{
|
| - RegionTree* current = s_regionTree;
|
| - while (current) {
|
| - Address base = current->m_region->base();
|
| - if (address < base) {
|
| - current = current->m_left;
|
| - continue;
|
| - }
|
| - if (address >= base + current->m_region->size()) {
|
| - current = current->m_right;
|
| - continue;
|
| - }
|
| - ASSERT(current->m_region->contains(address));
|
| - return current->m_region;
|
| - }
|
| - return nullptr;
|
| -}
|
| -
|
| -void Heap::RegionTree::add(RegionTree* newTree, RegionTree** context)
|
| -{
|
| - ASSERT(newTree);
|
| - Address base = newTree->m_region->base();
|
| - for (RegionTree* current = *context; current; current = *context) {
|
| - ASSERT(!current->m_region->contains(base));
|
| - context = (base < current->m_region->base()) ? ¤t->m_left : ¤t->m_right;
|
| - }
|
| - *context = newTree;
|
| -}
|
| -
|
| -void Heap::RegionTree::remove(PageMemoryRegion* region, RegionTree** context)
|
| -{
|
| - ASSERT(region);
|
| - ASSERT(context);
|
| - Address base = region->base();
|
| - RegionTree* current = *context;
|
| - for (; current; current = *context) {
|
| - if (region == current->m_region)
|
| - break;
|
| - context = (base < current->m_region->base()) ? ¤t->m_left : ¤t->m_right;
|
| - }
|
| -
|
| - // Shutdown via detachMainThread might not have populated the region tree.
|
| - if (!current)
|
| - return;
|
| -
|
| - *context = nullptr;
|
| - if (current->m_left) {
|
| - add(current->m_left, context);
|
| - current->m_left = nullptr;
|
| - }
|
| - if (current->m_right) {
|
| - add(current->m_right, context);
|
| - current->m_right = nullptr;
|
| - }
|
| - delete current;
|
| -}
|
| -
|
| -void Heap::resetHeapCounters()
|
| -{
|
| - ASSERT(ThreadState::current()->isInGC());
|
| -
|
| - Heap::reportMemoryUsageForTracing();
|
| -
|
| - s_objectSizeAtLastGC = s_allocatedObjectSize + s_markedObjectSize;
|
| - s_partitionAllocSizeAtLastGC = WTF::Partitions::totalSizeOfCommittedPages();
|
| - s_allocatedObjectSize = 0;
|
| - s_markedObjectSize = 0;
|
| - s_persistentCountAtLastGC = s_persistentCount;
|
| - s_collectedPersistentCount = 0;
|
| -}
|
| -
|
| -CallbackStack* Heap::s_markingStack;
|
| -CallbackStack* Heap::s_postMarkingCallbackStack;
|
| -CallbackStack* Heap::s_globalWeakCallbackStack;
|
| -CallbackStack* Heap::s_ephemeronStack;
|
| -HeapDoesNotContainCache* Heap::s_heapDoesNotContainCache;
|
| -bool Heap::s_shutdownCalled = false;
|
| -FreePagePool* Heap::s_freePagePool;
|
| -OrphanedPagePool* Heap::s_orphanedPagePool;
|
| -Heap::RegionTree* Heap::s_regionTree = nullptr;
|
| -size_t Heap::s_allocatedSpace = 0;
|
| -size_t Heap::s_allocatedObjectSize = 0;
|
| -size_t Heap::s_objectSizeAtLastGC = 0;
|
| -size_t Heap::s_markedObjectSize = 0;
|
| -size_t Heap::s_markedObjectSizeAtLastCompleteSweep = 0;
|
| -size_t Heap::s_persistentCount = 0;
|
| -size_t Heap::s_persistentCountAtLastGC = 0;
|
| -size_t Heap::s_collectedPersistentCount = 0;
|
| -size_t Heap::s_partitionAllocSizeAtLastGC = 0;
|
| -double Heap::s_estimatedMarkingTimePerByte = 0.0;
|
| -
|
| } // namespace blink
|
|
|