Index: third_party/WebKit/Source/platform/heap/HeapCompact.cpp |
diff --git a/third_party/WebKit/Source/platform/heap/HeapCompact.cpp b/third_party/WebKit/Source/platform/heap/HeapCompact.cpp |
deleted file mode 100644 |
index 43f2d2283ef72598229a152879bd69bbc001aa69..0000000000000000000000000000000000000000 |
--- a/third_party/WebKit/Source/platform/heap/HeapCompact.cpp |
+++ /dev/null |
@@ -1,479 +0,0 @@ |
-// Copyright 2016 The Chromium Authors. All rights reserved. |
-// Use of this source code is governed by a BSD-style license that can be |
-// found in the LICENSE file. |
- |
-#include "platform/heap/HeapCompact.h" |
- |
-#include "platform/RuntimeEnabledFeatures.h" |
-#include "platform/heap/Heap.h" |
-#include "platform/heap/SparseHeapBitmap.h" |
-#include "wtf/CurrentTime.h" |
-#include "wtf/HashMap.h" |
-#include "wtf/HashSet.h" |
-#include "wtf/Vector.h" |
- |
-namespace blink { |
- |
-bool HeapCompact::s_forceCompactionGC = false; |
- |
-// The real worker behind heap compaction, recording references to movable |
-// objects ("slots".) When the objects end up being compacted and moved, |
-// relocate() will adjust the slots to point to the new location of the |
-// object along with handling fixups for interior pointers. |
-// |
-// The "fixups" object is created and maintained for the lifetime of one |
-// heap compaction-enhanced GC. |
-class HeapCompact::MovableObjectFixups final { |
- public: |
- static std::unique_ptr<MovableObjectFixups> create() { |
- return WTF::wrapUnique(new MovableObjectFixups); |
- } |
- |
- ~MovableObjectFixups() {} |
- |
- // For the arenas being compacted, record all pages belonging to them. |
- // This is needed to handle 'interior slots', pointers that themselves |
- // can move (independently from the reference the slot points to.) |
- void addCompactingPage(BasePage* page) { |
- DCHECK(!page->isLargeObjectPage()); |
- m_relocatablePages.add(page); |
- } |
- |
- void addInteriorFixup(MovableReference* slot) { |
- auto it = m_interiorFixups.find(slot); |
- // Ephemeron fixpoint iterations may cause repeated registrations. |
- if (UNLIKELY(it != m_interiorFixups.end())) { |
- DCHECK(!it->value); |
- return; |
- } |
- m_interiorFixups.add(slot, nullptr); |
- LOG_HEAP_COMPACTION("Interior slot: %p\n", slot); |
- Address slotAddress = reinterpret_cast<Address>(slot); |
- if (!m_interiors) { |
- m_interiors = SparseHeapBitmap::create(slotAddress); |
- return; |
- } |
- m_interiors->add(slotAddress); |
- } |
- |
- void add(MovableReference* slot) { |
- MovableReference reference = *slot; |
- BasePage* refPage = pageFromObject(reference); |
- // Nothing to compact on a large object's page. |
- if (refPage->isLargeObjectPage()) |
- return; |
- |
-#if DCHECK_IS_ON() |
- DCHECK(HeapCompact::isCompactableArena(refPage->arena()->arenaIndex())); |
- auto it = m_fixups.find(reference); |
- DCHECK(it == m_fixups.end() || it->value == slot); |
-#endif |
- |
- // TODO: when updateHeapResidency() becomes more discriminating about |
- // leaving out arenas that aren't worth compacting, a check for |
- // isCompactingArena() would be appropriate here, leaving early if |
- // |refPage|'s arena isn't in the set. |
- |
- m_fixups.add(reference, slot); |
- |
- // Note: |slot| will reside outside the Oilpan heap if it is a |
- // PersistentHeapCollectionBase. Hence pageFromObject() cannot be |
- // used, as it sanity checks the |BasePage| it returns. Simply |
- // derive the raw BasePage address here and check if it is a member |
- // of the compactable and relocatable page address set. |
- Address slotAddress = reinterpret_cast<Address>(slot); |
- BasePage* slotPage = reinterpret_cast<BasePage*>( |
- blinkPageAddress(slotAddress) + blinkGuardPageSize); |
- if (LIKELY(!m_relocatablePages.contains(slotPage))) |
- return; |
-#if ENABLE(ASSERT) |
- slotPage->contains(slotAddress); |
-#endif |
- // Unlikely case, the slot resides on a compacting arena's page. |
- // => It is an 'interior slot' (interior to a movable backing store.) |
- // Record it as an interior slot, which entails: |
- // |
- // - Storing it in the interior map, which maps the slot to |
- // its (eventual) location. Initially nullptr. |
- // - Mark it as being interior pointer within the page's |
- // "interior" bitmap. This bitmap is used when moving a backing |
- // store, quickly/ier checking if interior slots will have to |
- // be additionally redirected. |
- addInteriorFixup(slot); |
- } |
- |
- void addFixupCallback(MovableReference reference, |
- MovingObjectCallback callback, |
- void* callbackData) { |
- DCHECK(!m_fixupCallbacks.contains(reference)); |
- m_fixupCallbacks.add(reference, std::pair<void*, MovingObjectCallback>( |
- callbackData, callback)); |
- } |
- |
- void relocateInteriorFixups(Address from, Address to, size_t size) { |
- SparseHeapBitmap* range = m_interiors->hasRange(from, size); |
- if (LIKELY(!range)) |
- return; |
- |
- // Scan through the payload, looking for interior pointer slots |
- // to adjust. If the backing store of such an interior slot hasn't |
- // been moved already, update the slot -> real location mapping. |
- // When the backing store is eventually moved, it'll use that location. |
- // |
- for (size_t offset = 0; offset < size; offset += sizeof(void*)) { |
- if (!range->isSet(from + offset)) |
- continue; |
- MovableReference* slot = |
- reinterpret_cast<MovableReference*>(from + offset); |
- auto it = m_interiorFixups.find(slot); |
- if (it == m_interiorFixups.end()) |
- continue; |
- |
- // TODO: with the right sparse bitmap representation, it could be possible |
- // to quickly determine if we've now stepped past the last address |
- // that needed fixup in [address, address + size). Breaking out of this |
- // loop might be worth doing for hash table backing stores with a very |
- // low load factor. But interior fixups are rare. |
- |
- // If |slot|'s mapping is set, then the slot has been adjusted already. |
- if (it->value) |
- continue; |
- Address fixup = to + offset; |
- LOG_HEAP_COMPACTION("Range interior fixup: %p %p %p\n", from + offset, |
- it->value, fixup); |
- // Fill in the relocated location of the original slot at |slot|. |
- // when the backing store corresponding to |slot| is eventually |
- // moved/compacted, it'll update |to + offset| with a pointer to the |
- // moved backing store. |
- m_interiorFixups.set(slot, fixup); |
- } |
- } |
- |
- void relocate(Address from, Address to) { |
- auto it = m_fixups.find(from); |
- DCHECK(it != m_fixups.end()); |
-#if DCHECK_IS_ON() |
- BasePage* fromPage = pageFromObject(from); |
- DCHECK(m_relocatablePages.contains(fromPage)); |
-#endif |
- MovableReference* slot = reinterpret_cast<MovableReference*>(it->value); |
- auto interior = m_interiorFixups.find(slot); |
- if (interior != m_interiorFixups.end()) { |
- MovableReference* slotLocation = |
- reinterpret_cast<MovableReference*>(interior->value); |
- if (!slotLocation) { |
- m_interiorFixups.set(slot, to); |
- } else { |
- LOG_HEAP_COMPACTION("Redirected slot: %p => %p\n", slot, slotLocation); |
- slot = slotLocation; |
- } |
- } |
- // If the slot has subsequently been updated, a prefinalizer or |
- // a destructor having mutated and expanded/shrunk the collection, |
- // do not update and relocate the slot -- |from| is no longer valid |
- // and referenced. |
- // |
- // The slot's contents may also have been cleared during weak processing; |
- // no work to be done in that case either. |
- if (UNLIKELY(*slot != from)) { |
- LOG_HEAP_COMPACTION( |
- "No relocation: slot = %p, *slot = %p, from = %p, to = %p\n", slot, |
- *slot, from, to); |
-#if DCHECK_IS_ON() |
- // Verify that the already updated slot is valid, meaning: |
- // - has been cleared. |
- // - has been updated & expanded with a large object backing store. |
- // - has been updated with a larger, freshly allocated backing store. |
- // (on a fresh page in a compactable arena that is not being |
- // compacted.) |
- if (!*slot) |
- return; |
- BasePage* slotPage = pageFromObject(*slot); |
- DCHECK( |
- slotPage->isLargeObjectPage() || |
- (HeapCompact::isCompactableArena(slotPage->arena()->arenaIndex()) && |
- !m_relocatablePages.contains(slotPage))); |
-#endif |
- return; |
- } |
- *slot = to; |
- |
- size_t size = 0; |
- auto callback = m_fixupCallbacks.find(from); |
- if (UNLIKELY(callback != m_fixupCallbacks.end())) { |
- size = HeapObjectHeader::fromPayload(to)->payloadSize(); |
- callback->value.second(callback->value.first, from, to, size); |
- } |
- |
- if (!m_interiors) |
- return; |
- |
- if (!size) |
- size = HeapObjectHeader::fromPayload(to)->payloadSize(); |
- relocateInteriorFixups(from, to, size); |
- } |
- |
-#if DEBUG_HEAP_COMPACTION |
- void dumpDebugStats() { |
- LOG_HEAP_COMPACTION( |
- "Fixups: pages=%u objects=%u callbacks=%u interior-size=%zu" |
- " interiors-f=%u\n", |
- m_relocatablePages.size(), m_fixups.size(), m_fixupCallbacks.size(), |
- m_interiors ? m_interiors->intervalCount() : 0, |
- m_interiorFixups.size()); |
- } |
-#endif |
- |
- private: |
- MovableObjectFixups() {} |
- |
- // Tracking movable and updatable references. For now, we keep a |
- // map which for each movable object, recording the slot that |
- // points to it. Upon moving the object, that slot needs to be |
- // updated. |
- // |
- // (TODO: consider in-place updating schemes.) |
- HashMap<MovableReference, MovableReference*> m_fixups; |
- |
- // Map from movable reference to callbacks that need to be invoked |
- // when the object moves. |
- HashMap<MovableReference, std::pair<void*, MovingObjectCallback>> |
- m_fixupCallbacks; |
- |
- // Slot => relocated slot/final location. |
- HashMap<MovableReference*, Address> m_interiorFixups; |
- |
- // All pages that are being compacted. |
- HashSet<BasePage*> m_relocatablePages; |
- |
- std::unique_ptr<SparseHeapBitmap> m_interiors; |
-}; |
- |
-HeapCompact::HeapCompact() |
- : m_doCompact(false), |
- m_gcCountSinceLastCompaction(0), |
- m_threadCount(0), |
- m_freeListSize(0), |
- m_compactableArenas(0u), |
- m_freedPages(0), |
- m_freedSize(0) |
-#if DEBUG_LOG_HEAP_COMPACTION_RUNNING_TIME |
- , |
- m_startCompactionTimeMS(0) |
-#endif |
-{ |
- // The heap compaction implementation assumes the contiguous range, |
- // |
- // [Vector1ArenaIndex, HashTableArenaIndex] |
- // |
- // in a few places. Use static asserts here to not have that assumption |
- // be silently invalidated by ArenaIndices changes. |
- static_assert(BlinkGC::Vector1ArenaIndex + 3 == BlinkGC::Vector4ArenaIndex, |
- "unexpected ArenaIndices ordering"); |
- static_assert( |
- BlinkGC::Vector4ArenaIndex + 1 == BlinkGC::InlineVectorArenaIndex, |
- "unexpected ArenaIndices ordering"); |
- static_assert( |
- BlinkGC::InlineVectorArenaIndex + 1 == BlinkGC::HashTableArenaIndex, |
- "unexpected ArenaIndices ordering"); |
-} |
- |
-HeapCompact::~HeapCompact() {} |
- |
-HeapCompact::MovableObjectFixups& HeapCompact::fixups() { |
- if (!m_fixups) |
- m_fixups = MovableObjectFixups::create(); |
- return *m_fixups; |
-} |
- |
-bool HeapCompact::shouldCompact(ThreadState* state, |
- BlinkGC::GCType gcType, |
- BlinkGC::GCReason reason) { |
-#if !ENABLE_HEAP_COMPACTION |
- return false; |
-#else |
- if (!RuntimeEnabledFeatures::heapCompactionEnabled()) |
- return false; |
- |
- LOG_HEAP_COMPACTION("shouldCompact(): gc=%s count=%zu free=%zu\n", |
- ThreadState::gcReasonString(reason), |
- m_gcCountSinceLastCompaction, m_freeListSize); |
- m_gcCountSinceLastCompaction++; |
- // It is only safe to compact during non-conservative GCs. |
- // TODO: for the main thread, limit this further to only idle GCs. |
- if (reason != BlinkGC::IdleGC && reason != BlinkGC::PreciseGC && |
- reason != BlinkGC::ForcedGC) |
- return false; |
- |
- const ThreadHeap& heap = state->heap(); |
- // If any of the participating threads require a stack scan, |
- // do not compact. |
- // |
- // Why? Should the stack contain an iterator pointing into its |
- // associated backing store, its references wouldn't be |
- // correctly relocated. |
- for (ThreadState* state : heap.threads()) { |
- if (state->stackState() == BlinkGC::HeapPointersOnStack) { |
- return false; |
- } |
- } |
- |
- // Compaction enable rules: |
- // - It's been a while since the last time. |
- // - "Considerable" amount of heap memory is bound up in freelist |
- // allocations. For now, use a fixed limit irrespective of heap |
- // size. |
- // |
- // As this isn't compacting all arenas, the cost of doing compaction |
- // isn't a worry as it will additionally only be done by idle GCs. |
- // TODO: add some form of compaction overhead estimate to the marking |
- // time estimate. |
- |
- updateHeapResidency(state); |
- |
-#if STRESS_TEST_HEAP_COMPACTION |
- // Exercise the handling of object movement by compacting as |
- // often as possible. |
- return true; |
-#else |
- return s_forceCompactionGC || |
- (m_gcCountSinceLastCompaction > kGCCountSinceLastCompactionThreshold && |
- m_freeListSize > kFreeListSizeThreshold); |
-#endif |
-#endif |
-} |
- |
-BlinkGC::GCType HeapCompact::initialize(ThreadState* state) { |
- DCHECK(RuntimeEnabledFeatures::heapCompactionEnabled()); |
- LOG_HEAP_COMPACTION("Compacting: free=%zu\n", m_freeListSize); |
- m_doCompact = true; |
- m_freedPages = 0; |
- m_freedSize = 0; |
- m_threadCount = state->heap().threads().size(); |
- m_fixups.reset(); |
- m_gcCountSinceLastCompaction = 0; |
- s_forceCompactionGC = false; |
- return BlinkGC::GCWithSweepCompaction; |
-} |
- |
-void HeapCompact::registerMovingObjectReference(MovableReference* slot) { |
- if (!m_doCompact) |
- return; |
- |
- fixups().add(slot); |
-} |
- |
-void HeapCompact::registerMovingObjectCallback(MovableReference reference, |
- MovingObjectCallback callback, |
- void* callbackData) { |
- if (!m_doCompact) |
- return; |
- |
- fixups().addFixupCallback(reference, callback, callbackData); |
-} |
- |
-void HeapCompact::updateHeapResidency(ThreadState* threadState) { |
- size_t totalArenaSize = 0; |
- size_t totalFreeListSize = 0; |
- |
- m_compactableArenas = 0; |
-#if DEBUG_HEAP_FREELIST |
- LOG_HEAP_FREELIST("Arena residencies: {"); |
-#endif |
- for (int i = BlinkGC::Vector1ArenaIndex; i <= BlinkGC::HashTableArenaIndex; |
- ++i) { |
- NormalPageArena* arena = |
- static_cast<NormalPageArena*>(threadState->arena(i)); |
- size_t arenaSize = arena->arenaSize(); |
- size_t freeListSize = arena->freeListSize(); |
- totalArenaSize += arenaSize; |
- totalFreeListSize += freeListSize; |
- LOG_HEAP_FREELIST("%d: [%zu, %zu], ", i, arenaSize, freeListSize); |
- // TODO: be more discriminating and consider arena |
- // load factor, effectiveness of past compactions etc. |
- if (!arenaSize) |
- continue; |
- // Mark the arena as compactable. |
- m_compactableArenas |= (0x1u << (BlinkGC::Vector1ArenaIndex + i)); |
- } |
- LOG_HEAP_FREELIST("}\nTotal = %zu, Free = %zu\n", totalArenaSize, |
- totalFreeListSize); |
- |
- // TODO(sof): consider smoothing the reported sizes. |
- m_freeListSize = totalFreeListSize; |
-} |
- |
-void HeapCompact::finishedArenaCompaction(NormalPageArena* arena, |
- size_t freedPages, |
- size_t freedSize) { |
- if (!m_doCompact) |
- return; |
- |
- m_freedPages += freedPages; |
- m_freedSize += freedSize; |
-} |
- |
-void HeapCompact::relocate(Address from, Address to) { |
- DCHECK(m_fixups); |
- m_fixups->relocate(from, to); |
-} |
- |
-void HeapCompact::startThreadCompaction() { |
- if (!m_doCompact) |
- return; |
-#if DEBUG_LOG_HEAP_COMPACTION_RUNNING_TIME |
- MutexLocker locker(m_mutex); |
- if (!m_startCompactionTimeMS) |
- m_startCompactionTimeMS = WTF::currentTimeMS(); |
-#endif |
-} |
- |
-void HeapCompact::finishThreadCompaction() { |
- if (!m_doCompact) |
- return; |
- |
- MutexLocker locker(m_mutex); |
- // Final one clears out. |
- if (!--m_threadCount) { |
-#if DEBUG_HEAP_COMPACTION |
- if (m_fixups) |
- m_fixups->dumpDebugStats(); |
-#endif |
- m_fixups.reset(); |
- m_doCompact = false; |
-#if DEBUG_LOG_HEAP_COMPACTION_RUNNING_TIME |
- double end = WTF::currentTimeMS(); |
- LOG_HEAP_COMPACTION_INTERNAL( |
- "Compaction stats: time=%gms, pages freed=%zu, size=%zu\n", |
- end - m_startCompactionTimeMS, m_freedPages, m_freedSize); |
- m_startCompactionTimeMS = 0; |
-#else |
- LOG_HEAP_COMPACTION("Compaction stats: freed pages=%zu size=%zu\n", |
- m_freedPages, m_freedSize); |
-#endif |
- // Compaction has been completed by all participating threads, unblock |
- // them all. |
- m_finished.broadcast(); |
- } else { |
- // Letting a thread return here and let it exit its safe point opens up |
- // the possibility of it accessing heaps of other threads that are |
- // still being compacted. It is not in a valid state until objects have |
- // all been moved together, hence all GC-participating threads must |
- // complete compaction together. Grab the condition variable and wait. |
- m_finished.wait(m_mutex); |
- } |
-} |
- |
-void HeapCompact::addCompactingPage(BasePage* page) { |
- DCHECK(m_doCompact); |
- DCHECK(isCompactingArena(page->arena()->arenaIndex())); |
- fixups().addCompactingPage(page); |
-} |
- |
-bool HeapCompact::scheduleCompactionGCForTesting(bool value) { |
- bool current = s_forceCompactionGC; |
- s_forceCompactionGC = value; |
- return current; |
-} |
- |
-} // namespace blink |