Skip to content

Commit

Permalink
[heap] Clean-up some weak map entries in scavenger
Browse files Browse the repository at this point in the history
This change enables clean-up of weak map entries in the
scavenger of the weak map is in the young generation.
With this change, the scavenger treats keys in ephemerons as
weak instead of strong, but does not implement full ephemeron
semantics: Values are treated always as strong, independently
of whether the key is live or not.

This approach ensures that no value is cleaned up accidentally.
After scavenging, all entries with dead keys are removed from
weak maps. After that, some values that are not referenced anywhere
anymore might still be in the heap, and those can be cleaned up
in the next scavenge.

What the scavenger does, amounts to one iteration of the
fixed-point algorithm required to implement ephemeron semantics.
We hope that this is a reasonable trade-off between time spent
tracing and cleaned-up entries.

This change does not affect weak maps that reside in old space.

Bug: v8:8557
Change-Id: Ic5618b3b863ad8c314c87449571150e756fecbf0
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1467182
Commit-Queue: Sigurd Schneider <sigurds@chromium.org>
Reviewed-by: Ulan Degenbaev <ulan@chromium.org>
Cr-Commit-Position: refs/heads/master@{#60101}
  • Loading branch information
sigurdschneider authored and Commit Bot committed Mar 7, 2019
1 parent 676014b commit 4e6a1a7
Show file tree
Hide file tree
Showing 7 changed files with 82 additions and 22 deletions.
14 changes: 14 additions & 0 deletions src/heap/heap.h
Expand Up @@ -2347,6 +2347,20 @@ class HeapObjectAllocationTracker {
virtual ~HeapObjectAllocationTracker() = default;
};

template <typename T>
T ForwardingAddress(T heap_obj) {
MapWord map_word = heap_obj->map_word();

if (map_word.IsForwardingAddress()) {
return T::cast(map_word.ToForwardingAddress());
} else if (Heap::InFromPage(heap_obj)) {
return T();
} else {
// TODO(ulan): Support minor mark-compactor here.
return heap_obj;
}
}

} // namespace internal
} // namespace v8

Expand Down
16 changes: 0 additions & 16 deletions src/heap/incremental-marking.cc
Expand Up @@ -608,22 +608,6 @@ void IncrementalMarking::UpdateMarkingWorklistAfterScavenge() {
UpdateWeakReferencesAfterScavenge();
}

namespace {
template <typename T>
T ForwardingAddress(T heap_obj) {
MapWord map_word = heap_obj->map_word();

if (map_word.IsForwardingAddress()) {
return T::cast(map_word.ToForwardingAddress());
} else if (Heap::InFromPage(heap_obj)) {
return T();
} else {
// TODO(ulan): Support minor mark-compactor here.
return heap_obj;
}
}
} // namespace

void IncrementalMarking::UpdateWeakReferencesAfterScavenge() {
weak_objects_->weak_references.Update(
[](std::pair<HeapObject, HeapObjectSlot> slot_in,
Expand Down
15 changes: 15 additions & 0 deletions src/heap/scavenger-inl.h
Expand Up @@ -480,6 +480,21 @@ void ScavengeVisitor::VisitPointersImpl(HeapObject host, TSlot start,
}
}

int ScavengeVisitor::VisitEphemeronHashTable(Map map,
EphemeronHashTable table) {
// Register table with the scavenger, so it can take care of the weak keys
// later. This allows to only iterate the tables' values, which are treated
// as strong independetly of whether the key is live.
scavenger_->AddEphemeronHashTable(table);
for (int i = 0; i < table->Capacity(); i++) {
ObjectSlot value_slot =
table->RawFieldOfElementAt(EphemeronHashTable::EntryToValueIndex(i));
VisitPointer(table, value_slot);
}

return table->SizeFromMap(map);
}

} // namespace internal
} // namespace v8

Expand Down
43 changes: 39 additions & 4 deletions src/heap/scavenger.cc
Expand Up @@ -185,9 +185,10 @@ void ScavengerCollector::CollectGarbage() {
OneshotBarrier barrier(base::TimeDelta::FromMilliseconds(kMaxWaitTimeMs));
Scavenger::CopiedList copied_list(num_scavenge_tasks);
Scavenger::PromotionList promotion_list(num_scavenge_tasks);
EphemeronTableList ephemeron_table_list(num_scavenge_tasks);
for (int i = 0; i < num_scavenge_tasks; i++) {
scavengers[i] = new Scavenger(this, heap_, is_logging, &copied_list,
&promotion_list, i);
&promotion_list, &ephemeron_table_list, i);
job.AddTask(new ScavengingTask(heap_, scavengers[i], &barrier));
}

Expand Down Expand Up @@ -280,8 +281,7 @@ void ScavengerCollector::CollectGarbage() {
}
}

ScavengeWeakObjectRetainer weak_object_retainer;
heap_->ProcessYoungWeakReferences(&weak_object_retainer);
ProcessWeakReferences(&ephemeron_table_list);

// Set age mark.
heap_->new_space_->set_age_mark(heap_->new_space()->top());
Expand Down Expand Up @@ -349,11 +349,12 @@ int ScavengerCollector::NumberOfScavengeTasks() {

Scavenger::Scavenger(ScavengerCollector* collector, Heap* heap, bool is_logging,
CopiedList* copied_list, PromotionList* promotion_list,
int task_id)
EphemeronTableList* ephemeron_table_list, int task_id)
: collector_(collector),
heap_(heap),
promotion_list_(promotion_list, task_id),
copied_list_(copied_list, task_id),
ephemeron_table_list_(ephemeron_table_list, task_id),
local_pretenuring_feedback_(kInitialLocalPretenuringFeedbackCapacity),
copied_size_(0),
promoted_size_(0),
Expand Down Expand Up @@ -440,12 +441,46 @@ void Scavenger::Process(OneshotBarrier* barrier) {
} while (!done);
}

void ScavengerCollector::ProcessWeakReferences(
EphemeronTableList* ephemeron_table_list) {
ScavengeWeakObjectRetainer weak_object_retainer;
heap_->ProcessYoungWeakReferences(&weak_object_retainer);
ClearYoungEphemerons(ephemeron_table_list);
}

// Clears ephemerons contained in {EphemeronHashTable}s in young generation.
void ScavengerCollector::ClearYoungEphemerons(
EphemeronTableList* ephemeron_table_list) {
ephemeron_table_list->Iterate([this](EphemeronHashTable table) {
for (int i = 0; i < table->Capacity(); i++) {
ObjectSlot key_slot =
table->RawFieldOfElementAt(EphemeronHashTable::EntryToIndex(i));
Object key = *key_slot;
if (key->IsHeapObject()) {
HeapObjectSlot key_slot_heap(key_slot);
if (IsUnscavengedHeapObject(heap_, key_slot)) {
table->RemoveEntry(i);
} else {
HeapObject forwarded = ForwardingAddress(HeapObject::cast(key));
HeapObjectReference::Update(key_slot_heap, forwarded);
}
}
}
});
ephemeron_table_list->Clear();
}

void Scavenger::Finalize() {
heap()->MergeAllocationSitePretenuringFeedback(local_pretenuring_feedback_);
heap()->IncrementSemiSpaceCopiedObjectSize(copied_size_);
heap()->IncrementPromotedObjectsSize(promoted_size_);
collector_->MergeSurvivingNewLargeObjects(surviving_new_large_objects_);
allocator_.Finalize();
ephemeron_table_list_.FlushToGlobal();
}

void Scavenger::AddEphemeronHashTable(EphemeronHashTable table) {
ephemeron_table_list_.Push(table);
}

void RootScavengeVisitor::VisitRootPointer(Root root, const char* description,
Expand Down
13 changes: 11 additions & 2 deletions src/heap/scavenger.h
Expand Up @@ -27,6 +27,10 @@ using SurvivingNewLargeObjectsMap =
std::unordered_map<HeapObject, Map, Object::Hasher>;
using SurvivingNewLargeObjectMapEntry = std::pair<HeapObject, Map>;

constexpr int kEphemeronTableListSegmentSize = 128;
using EphemeronTableList =
Worklist<EphemeronHashTable, kEphemeronTableListSegmentSize>;

class ScavengerCollector {
public:
static const int kMaxScavengerTasks = 8;
Expand All @@ -42,6 +46,8 @@ class ScavengerCollector {

int NumberOfScavengeTasks();

void ProcessWeakReferences(EphemeronTableList* ephemeron_table_list);
void ClearYoungEphemerons(EphemeronTableList* ephemeron_table_list);
void HandleSurvivingNewLargeObjects();

Isolate* const isolate_;
Expand Down Expand Up @@ -109,10 +115,9 @@ class Scavenger {
static const int kCopiedListSegmentSize = 256;

using CopiedList = Worklist<ObjectAndSize, kCopiedListSegmentSize>;

Scavenger(ScavengerCollector* collector, Heap* heap, bool is_logging,
CopiedList* copied_list, PromotionList* promotion_list,
int task_id);
EphemeronTableList* ephemeron_table_list, int task_id);

// Entry point for scavenging an old generation page. For scavenging single
// objects see RootScavengingVisitor and ScavengeVisitor below.
Expand All @@ -125,6 +130,8 @@ class Scavenger {
// Finalize the Scavenger. Needs to be called from the main thread.
void Finalize();

void AddEphemeronHashTable(EphemeronHashTable table);

size_t bytes_copied() const { return copied_size_; }
size_t bytes_promoted() const { return promoted_size_; }

Expand Down Expand Up @@ -199,6 +206,7 @@ class Scavenger {
Heap* const heap_;
PromotionList::View promotion_list_;
CopiedList::View copied_list_;
EphemeronTableList::View ephemeron_table_list_;
Heap::PretenuringFeedbackMap local_pretenuring_feedback_;
size_t copied_size_;
size_t promoted_size_;
Expand Down Expand Up @@ -242,6 +250,7 @@ class ScavengeVisitor final : public NewSpaceVisitor<ScavengeVisitor> {

V8_INLINE void VisitCodeTarget(Code host, RelocInfo* rinfo) final;
V8_INLINE void VisitEmbeddedPointer(Code host, RelocInfo* rinfo) final;
V8_INLINE int VisitEphemeronHashTable(Map map, EphemeronHashTable object);

private:
template <typename TSlot>
Expand Down
2 changes: 2 additions & 0 deletions src/heap/worklist.h
Expand Up @@ -51,6 +51,8 @@ class Worklist {
return worklist_->LocalPushSegmentSize(task_id_);
}

void FlushToGlobal() { worklist_->FlushToGlobal(task_id_); }

private:
Worklist<EntryType, SEGMENT_SIZE>* worklist_;
int task_id_;
Expand Down
1 change: 1 addition & 0 deletions src/objects/hash-table.h
Expand Up @@ -345,6 +345,7 @@ class EphemeronHashTable

protected:
friend class MarkCompactCollector;
friend class ScavengerCollector;

OBJECT_CONSTRUCTORS(
EphemeronHashTable,
Expand Down

0 comments on commit 4e6a1a7

Please sign in to comment.