Commit 4e6a1a75 authored by Sigurd Schneider's avatar Sigurd Schneider Committed by Commit Bot

[heap] Clean-up some weak map entries in scavenger

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: 's avatarUlan Degenbaev <ulan@chromium.org>
Cr-Commit-Position: refs/heads/master@{#60101}
parent 676014b3
......@@ -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
......
......@@ -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,
......
......@@ -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
......
......@@ -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));
}
......@@ -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());
......@@ -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),
......@@ -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,
......
......@@ -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;
......@@ -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_;
......@@ -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.
......@@ -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_; }
......@@ -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_;
......@@ -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>
......
......@@ -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_;
......
......@@ -345,6 +345,7 @@ class EphemeronHashTable
protected:
friend class MarkCompactCollector;
friend class ScavengerCollector;
OBJECT_CONSTRUCTORS(
EphemeronHashTable,
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment