| Index: third_party/WebKit/Source/platform/heap/ThreadState.cpp
|
| diff --git a/third_party/WebKit/Source/platform/heap/ThreadState.cpp b/third_party/WebKit/Source/platform/heap/ThreadState.cpp
|
| index 6972289a81e285186324a9e51c408bcf758c3325..60939a77881ebddff143d09cb22abb5f1fae9553 100644
|
| --- a/third_party/WebKit/Source/platform/heap/ThreadState.cpp
|
| +++ b/third_party/WebKit/Source/platform/heap/ThreadState.cpp
|
| @@ -39,6 +39,7 @@
|
| #include "platform/heap/CallbackStack.h"
|
| #include "platform/heap/Handle.h"
|
| #include "platform/heap/Heap.h"
|
| +#include "platform/heap/PagePool.h"
|
| #include "platform/heap/SafePoint.h"
|
| #include "platform/heap/Visitor.h"
|
| #include "platform/web_memory_allocator_dump.h"
|
| @@ -78,6 +79,72 @@ uint8_t ThreadState::s_mainThreadStateStorage[sizeof(ThreadState)];
|
|
|
| const size_t defaultAllocatedObjectSizeThreshold = 100 * 1024;
|
|
|
| +const char* gcReasonString(BlinkGC::GCReason reason)
|
| +{
|
| + switch (reason) {
|
| + case BlinkGC::IdleGC:
|
| + return "IdleGC";
|
| + case BlinkGC::PreciseGC:
|
| + return "PreciseGC";
|
| + case BlinkGC::ConservativeGC:
|
| + return "ConservativeGC";
|
| + case BlinkGC::ForcedGC:
|
| + return "ForcedGC";
|
| + case BlinkGC::MemoryPressureGC:
|
| + return "MemoryPressureGC";
|
| + case BlinkGC::PageNavigationGC:
|
| + return "PageNavigationGC";
|
| + default:
|
| + NOTREACHED();
|
| + }
|
| + return "<Unknown>";
|
| +}
|
| +
|
| +class ParkThreadsScope final {
|
| + STACK_ALLOCATED();
|
| +public:
|
| + explicit ParkThreadsScope(ThreadState* state)
|
| + : m_state(state)
|
| + , m_shouldResumeThreads(false)
|
| + {
|
| + }
|
| +
|
| + bool parkThreads()
|
| + {
|
| + TRACE_EVENT0("blink_gc", "ThreadHeap::ParkThreadsScope");
|
| + const char* samplingState = TRACE_EVENT_GET_SAMPLING_STATE();
|
| + if (m_state->isMainThread())
|
| + TRACE_EVENT_SET_SAMPLING_STATE("blink_gc", "BlinkGCWaiting");
|
| +
|
| + // TODO(haraken): In an unlikely coincidence that two threads decide
|
| + // to collect garbage at the same time, avoid doing two GCs in
|
| + // a row and return false.
|
| + double startTime = WTF::currentTimeMS();
|
| +
|
| + m_shouldResumeThreads = m_state->heap().park();
|
| +
|
| + double timeForStoppingThreads = WTF::currentTimeMS() - startTime;
|
| + DEFINE_THREAD_SAFE_STATIC_LOCAL(CustomCountHistogram, timeToStopThreadsHistogram, new CustomCountHistogram("BlinkGC.TimeForStoppingThreads", 1, 1000, 50));
|
| + timeToStopThreadsHistogram.count(timeForStoppingThreads);
|
| +
|
| + if (m_state->isMainThread())
|
| + TRACE_EVENT_SET_NONCONST_SAMPLING_STATE(samplingState);
|
| + return m_shouldResumeThreads;
|
| + }
|
| +
|
| + ~ParkThreadsScope()
|
| + {
|
| + // Only cleanup if we parked all threads in which case the GC happened
|
| + // and we need to resume the other threads.
|
| + if (m_shouldResumeThreads)
|
| + m_state->heap().resume();
|
| + }
|
| +
|
| +private:
|
| + ThreadState* m_state;
|
| + bool m_shouldResumeThreads;
|
| +};
|
| +
|
| ThreadState::ThreadState(bool perThreadHeapEnabled)
|
| : m_thread(currentThread())
|
| , m_persistentRegion(wrapUnique(new PersistentRegion()))
|
| @@ -238,7 +305,7 @@ void ThreadState::runTerminationGC()
|
| int currentCount = getPersistentRegion()->numberOfPersistents();
|
| ASSERT(currentCount >= 0);
|
| while (currentCount != oldCount) {
|
| - ThreadHeap::collectGarbageForTerminatingThread(this);
|
| + collectGarbageForTerminatingThread();
|
| // Release the thread-local static persistents that were
|
| // instantiated while running the termination GC.
|
| releaseStaticPersistentNodes();
|
| @@ -265,7 +332,7 @@ void ThreadState::cleanupMainThread()
|
| // See comment below, clear out most garbage before releasing static
|
| // persistents should some of the finalizers depend on touching
|
| // these persistents.
|
| - ThreadHeap::collectAllGarbage();
|
| + collectAllGarbage();
|
| #endif
|
|
|
| releaseStaticPersistentNodes();
|
| @@ -279,7 +346,7 @@ void ThreadState::cleanupMainThread()
|
| // This is not needed for caches over non-Oilpan objects, as they're
|
| // not scanned by LSan due to being held in non-global storage
|
| // ("static" references inside functions/methods.)
|
| - ThreadHeap::collectAllGarbage();
|
| + collectAllGarbage();
|
| #endif
|
|
|
| // Finish sweeping before shutting down V8. Otherwise, some destructor
|
| @@ -622,7 +689,7 @@ void ThreadState::schedulePageNavigationGCIfNeeded(float estimatedRemovalRatio)
|
| #if PRINT_HEAP_STATS
|
| dataLogF("Scheduled MemoryPressureGC\n");
|
| #endif
|
| - ThreadHeap::collectGarbage(BlinkGC::HeapPointersOnStack, BlinkGC::GCWithoutSweep, BlinkGC::MemoryPressureGC);
|
| + collectGarbage(BlinkGC::HeapPointersOnStack, BlinkGC::GCWithoutSweep, BlinkGC::MemoryPressureGC);
|
| return;
|
| }
|
| if (shouldSchedulePageNavigationGC(estimatedRemovalRatio)) {
|
| @@ -666,7 +733,7 @@ void ThreadState::scheduleGCIfNeeded()
|
| #if PRINT_HEAP_STATS
|
| dataLogF("Scheduled MemoryPressureGC\n");
|
| #endif
|
| - ThreadHeap::collectGarbage(BlinkGC::HeapPointersOnStack, BlinkGC::GCWithoutSweep, BlinkGC::MemoryPressureGC);
|
| + collectGarbage(BlinkGC::HeapPointersOnStack, BlinkGC::GCWithoutSweep, BlinkGC::MemoryPressureGC);
|
| return;
|
| }
|
| }
|
| @@ -677,7 +744,7 @@ void ThreadState::scheduleGCIfNeeded()
|
| #if PRINT_HEAP_STATS
|
| dataLogF("Scheduled ConservativeGC\n");
|
| #endif
|
| - ThreadHeap::collectGarbage(BlinkGC::HeapPointersOnStack, BlinkGC::GCWithoutSweep, BlinkGC::ConservativeGC);
|
| + collectGarbage(BlinkGC::HeapPointersOnStack, BlinkGC::GCWithoutSweep, BlinkGC::ConservativeGC);
|
| return;
|
| }
|
| }
|
| @@ -723,7 +790,7 @@ void ThreadState::performIdleGC(double deadlineSeconds)
|
| }
|
|
|
| TRACE_EVENT2("blink_gc", "ThreadState::performIdleGC", "idleDeltaInSeconds", idleDeltaInSeconds, "estimatedMarkingTime", m_heap->heapStats().estimatedMarkingTime());
|
| - ThreadHeap::collectGarbage(BlinkGC::NoHeapPointersOnStack, BlinkGC::GCWithoutSweep, BlinkGC::IdleGC);
|
| + collectGarbage(BlinkGC::NoHeapPointersOnStack, BlinkGC::GCWithoutSweep, BlinkGC::IdleGC);
|
| }
|
|
|
| void ThreadState::performIdleLazySweep(double deadlineSeconds)
|
| @@ -899,13 +966,13 @@ void ThreadState::runScheduledGC(BlinkGC::StackState stackState)
|
|
|
| switch (gcState()) {
|
| case FullGCScheduled:
|
| - ThreadHeap::collectAllGarbage();
|
| + collectAllGarbage();
|
| break;
|
| case PreciseGCScheduled:
|
| - ThreadHeap::collectGarbage(BlinkGC::NoHeapPointersOnStack, BlinkGC::GCWithoutSweep, BlinkGC::PreciseGC);
|
| + collectGarbage(BlinkGC::NoHeapPointersOnStack, BlinkGC::GCWithoutSweep, BlinkGC::PreciseGC);
|
| break;
|
| case PageNavigationGCScheduled:
|
| - ThreadHeap::collectGarbage(BlinkGC::NoHeapPointersOnStack, BlinkGC::GCWithSweep, BlinkGC::PageNavigationGC);
|
| + collectGarbage(BlinkGC::NoHeapPointersOnStack, BlinkGC::GCWithSweep, BlinkGC::PageNavigationGC);
|
| break;
|
| case IdleGCScheduled:
|
| // Idle time GC will be scheduled by Blink Scheduler.
|
| @@ -1539,4 +1606,151 @@ void ThreadState::takeSnapshot(SnapshotType type)
|
| BlinkGCMemoryDumpProvider::instance()->currentProcessMemoryDump()->AddOwnershipEdge(classesDump->guid(), heapsDump->guid());
|
| }
|
|
|
| +void ThreadState::collectGarbage(BlinkGC::StackState stackState, BlinkGC::GCType gcType, BlinkGC::GCReason reason)
|
| +{
|
| + DCHECK_NE(gcType, BlinkGC::ThreadTerminationGC);
|
| +
|
| + // Nested collectGarbage() invocations aren't supported.
|
| + RELEASE_ASSERT(!isGCForbidden());
|
| + completeSweep();
|
| +
|
| + std::unique_ptr<Visitor> visitor = Visitor::create(this, gcType);
|
| +
|
| + SafePointScope safePointScope(stackState, this);
|
| +
|
| + // Resume all parked threads upon leaving this scope.
|
| + ParkThreadsScope parkThreadsScope(this);
|
| +
|
| + // Try to park the other threads. If we're unable to, bail out of the GC.
|
| + if (!parkThreadsScope.parkThreads())
|
| + return;
|
| +
|
| + ScriptForbiddenIfMainThreadScope scriptForbidden;
|
| +
|
| + TRACE_EVENT2("blink_gc,devtools.timeline", "BlinkGCMarking",
|
| + "lazySweeping", gcType == BlinkGC::GCWithoutSweep,
|
| + "gcReason", gcReasonString(reason));
|
| + TRACE_EVENT_SCOPED_SAMPLING_STATE("blink_gc", "BlinkGC");
|
| + double startTime = WTF::currentTimeMS();
|
| +
|
| + if (gcType == BlinkGC::TakeSnapshot)
|
| + BlinkGCMemoryDumpProvider::instance()->clearProcessDumpForCurrentGC();
|
| +
|
| + // Disallow allocation during garbage collection (but not during the
|
| + // finalization that happens when the visitorScope is torn down).
|
| + ThreadState::NoAllocationScope noAllocationScope(this);
|
| +
|
| + heap().commitCallbackStacks();
|
| + heap().preGC();
|
| +
|
| + StackFrameDepthScope stackDepthScope(&heap().stackFrameDepth());
|
| +
|
| + size_t totalObjectSize = heap().heapStats().allocatedObjectSize() + heap().heapStats().markedObjectSize();
|
| + if (gcType != BlinkGC::TakeSnapshot)
|
| + heap().resetHeapCounters();
|
| +
|
| + {
|
| + // Access to the CrossThreadPersistentRegion has to be prevented while
|
| + // marking and global weak processing is in progress. If not, threads
|
| + // not attached to Oilpan and participating in this GC are able
|
| + // to allocate & free PersistentNodes, something the marking phase isn't
|
| + // capable of handling.
|
| + CrossThreadPersistentRegion::LockScope persistentLock(ProcessHeap::crossThreadPersistentRegion());
|
| +
|
| + // 1. Trace persistent roots.
|
| + heap().visitPersistentRoots(visitor.get());
|
| +
|
| + // 2. Trace objects reachable from the stack. We do this independent of the
|
| + // given stackState since other threads might have a different stack state.
|
| + heap().visitStackRoots(visitor.get());
|
| +
|
| + // 3. Transitive closure to trace objects including ephemerons.
|
| + heap().processMarkingStack(visitor.get());
|
| +
|
| + heap().postMarkingProcessing(visitor.get());
|
| + heap().globalWeakProcessing(visitor.get());
|
| + }
|
| +
|
| + // 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.)
|
| + heap().getOrphanedPagePool()->decommitOrphanedPages();
|
| +
|
| + double markingTimeInMilliseconds = WTF::currentTimeMS() - startTime;
|
| + heap().heapStats().setEstimatedMarkingTimePerByte(totalObjectSize ? (markingTimeInMilliseconds / 1000 / totalObjectSize) : 0);
|
| +
|
| +#if PRINT_HEAP_STATS
|
| + dataLogF("ThreadHeap::collectGarbage (gcReason=%s, lazySweeping=%d, time=%.1lfms)\n", gcReasonString(reason), gcType == BlinkGC::GCWithoutSweep, markingTimeInMilliseconds);
|
| +#endif
|
| +
|
| + DEFINE_THREAD_SAFE_STATIC_LOCAL(CustomCountHistogram, markingTimeHistogram, new CustomCountHistogram("BlinkGC.CollectGarbage", 0, 10 * 1000, 50));
|
| + markingTimeHistogram.count(markingTimeInMilliseconds);
|
| + DEFINE_THREAD_SAFE_STATIC_LOCAL(CustomCountHistogram, totalObjectSpaceHistogram, new CustomCountHistogram("BlinkGC.TotalObjectSpace", 0, 4 * 1024 * 1024, 50));
|
| + totalObjectSpaceHistogram.count(ProcessHeap::totalAllocatedObjectSize() / 1024);
|
| + DEFINE_THREAD_SAFE_STATIC_LOCAL(CustomCountHistogram, totalAllocatedSpaceHistogram, new CustomCountHistogram("BlinkGC.TotalAllocatedSpace", 0, 4 * 1024 * 1024, 50));
|
| + totalAllocatedSpaceHistogram.count(ProcessHeap::totalAllocatedSpace() / 1024);
|
| + DEFINE_THREAD_SAFE_STATIC_LOCAL(EnumerationHistogram, gcReasonHistogram, new EnumerationHistogram("BlinkGC.GCReason", BlinkGC::NumberOfGCReason));
|
| + gcReasonHistogram.count(reason);
|
| +
|
| + heap().m_lastGCReason = reason;
|
| +
|
| + ThreadHeap::reportMemoryUsageHistogram();
|
| + WTF::Partitions::reportMemoryUsageHistogram();
|
| +
|
| + heap().postGC(gcType);
|
| + heap().decommitCallbackStacks();
|
| +}
|
| +
|
| +void ThreadState::collectGarbageForTerminatingThread()
|
| +{
|
| + {
|
| + // 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. VisitorScope will not enter also a safepoint scope for
|
| + // ThreadTerminationGC.
|
| + std::unique_ptr<Visitor> visitor = Visitor::create(this, BlinkGC::ThreadTerminationGC);
|
| +
|
| + ThreadState::NoAllocationScope noAllocationScope(this);
|
| +
|
| + heap().commitCallbackStacks();
|
| + preGC();
|
| +
|
| + // 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.
|
| + visitPersistents(visitor.get());
|
| +
|
| + // 2. Trace objects reachable from the thread's persistent roots
|
| + // including ephemerons.
|
| + heap().processMarkingStack(visitor.get());
|
| +
|
| + heap().postMarkingProcessing(visitor.get());
|
| + heap().globalWeakProcessing(visitor.get());
|
| +
|
| + postGC(BlinkGC::GCWithSweep);
|
| + heap().decommitCallbackStacks();
|
| + }
|
| + preSweep();
|
| +}
|
| +
|
| +void ThreadState::collectAllGarbage()
|
| +{
|
| + // We need to run multiple GCs to collect a chain of persistent handles.
|
| + size_t previousLiveObjects = 0;
|
| + for (int i = 0; i < 5; ++i) {
|
| + collectGarbage(BlinkGC::NoHeapPointersOnStack, BlinkGC::GCWithSweep, BlinkGC::ForcedGC);
|
| + size_t liveObjects = heap().heapStats().markedObjectSize();
|
| + if (liveObjects == previousLiveObjects)
|
| + break;
|
| + previousLiveObjects = liveObjects;
|
| + }
|
| +}
|
| +
|
| } // namespace blink
|
|
|