Commit 959e050c authored by mythria's avatar mythria Committed by Commit bot

Adds a scavenge GC pass to collect unmodified references

Adds a scavenge GC pass that collects unmodified references instead of
processing object groups. This mode can be controlled by setting
FLAG_scavenge_reclaim_unmodified_objects. By default this is turned off.
Also, modified a test case to suit the handle the new GC pass.

BUG=v8:4421
LOG=N

Review URL: https://codereview.chromium.org/1410593005

Cr-Commit-Position: refs/heads/master@{#31599}
parent 37f5e23b
...@@ -603,6 +603,13 @@ template <class T> class PersistentBase { ...@@ -603,6 +603,13 @@ template <class T> class PersistentBase {
*/ */
V8_INLINE void MarkPartiallyDependent(); V8_INLINE void MarkPartiallyDependent();
/**
* Marks the reference to this object as active. The scavenge garbage
* collection should not reclaim the objects marked as active.
* This bit is cleared after the each garbage collection pass.
*/
V8_INLINE void MarkActive();
V8_INLINE bool IsIndependent() const; V8_INLINE bool IsIndependent() const;
/** Checks if the handle holds the only reference to an object. */ /** Checks if the handle holds the only reference to an object. */
...@@ -5960,6 +5967,13 @@ class V8_EXPORT Isolate { ...@@ -5960,6 +5967,13 @@ class V8_EXPORT Isolate {
*/ */
void VisitHandlesForPartialDependence(PersistentHandleVisitor* visitor); void VisitHandlesForPartialDependence(PersistentHandleVisitor* visitor);
/**
* Iterates through all the persistent handles in the current isolate's heap
* that have class_ids and are weak to be marked as inactive if there is no
* pending activity for the handle.
*/
void VisitWeakHandles(PersistentHandleVisitor* visitor);
private: private:
template <class K, class V, class Traits> template <class K, class V, class Traits>
friend class PersistentValueMapBase; friend class PersistentValueMapBase;
...@@ -7042,6 +7056,7 @@ class Internals { ...@@ -7042,6 +7056,7 @@ class Internals {
static const int kNodeStateIsNearDeathValue = 4; static const int kNodeStateIsNearDeathValue = 4;
static const int kNodeIsIndependentShift = 3; static const int kNodeIsIndependentShift = 3;
static const int kNodeIsPartiallyDependentShift = 4; static const int kNodeIsPartiallyDependentShift = 4;
static const int kNodeIsActiveShift = 4;
static const int kJSObjectType = 0xb7; static const int kJSObjectType = 0xb7;
static const int kFirstNonstringType = 0x80; static const int kFirstNonstringType = 0x80;
...@@ -7368,6 +7383,15 @@ void PersistentBase<T>::MarkPartiallyDependent() { ...@@ -7368,6 +7383,15 @@ void PersistentBase<T>::MarkPartiallyDependent() {
} }
template <class T>
void PersistentBase<T>::MarkActive() {
typedef internal::Internals I;
if (this->IsEmpty()) return;
I::UpdateNodeFlag(reinterpret_cast<internal::Object**>(this->val_), true,
I::kNodeIsActiveShift);
}
template <class T> template <class T>
void PersistentBase<T>::SetWrapperClassId(uint16_t class_id) { void PersistentBase<T>::SetWrapperClassId(uint16_t class_id) {
typedef internal::Internals I; typedef internal::Internals I;
......
...@@ -7593,6 +7593,15 @@ void Isolate::VisitHandlesForPartialDependence( ...@@ -7593,6 +7593,15 @@ void Isolate::VisitHandlesForPartialDependence(
} }
void Isolate::VisitWeakHandles(PersistentHandleVisitor* visitor) {
i::Isolate* isolate = reinterpret_cast<i::Isolate*>(this);
i::DisallowHeapAllocation no_allocation;
VisitorAdapter visitor_adapter(visitor);
isolate->global_handles()->IterateWeakRootsInNewSpaceWithClassIds(
&visitor_adapter);
}
String::Utf8Value::Utf8Value(v8::Local<v8::Value> obj) String::Utf8Value::Utf8Value(v8::Local<v8::Value> obj)
: str_(NULL), length_(0) { : str_(NULL), length_(0) {
if (obj.IsEmpty()) return; if (obj.IsEmpty()) return;
......
...@@ -683,6 +683,8 @@ DEFINE_BOOL(verify_heap, false, "verify heap pointers before and after GC") ...@@ -683,6 +683,8 @@ DEFINE_BOOL(verify_heap, false, "verify heap pointers before and after GC")
#endif #endif
DEFINE_BOOL(move_object_start, false, "enable moving of object starts") DEFINE_BOOL(move_object_start, false, "enable moving of object starts")
DEFINE_BOOL(memory_reducer, true, "use memory reducer") DEFINE_BOOL(memory_reducer, true, "use memory reducer")
DEFINE_BOOL(scavenge_reclaim_unmodified_objects, false,
"remove unmodified and unreferenced objects")
// counters.cc // counters.cc
DEFINE_INT(histogram_interval, 600000, DEFINE_INT(histogram_interval, 600000,
......
...@@ -54,6 +54,8 @@ class GlobalHandles::Node { ...@@ -54,6 +54,8 @@ class GlobalHandles::Node {
Internals::kNodeIsIndependentShift); Internals::kNodeIsIndependentShift);
STATIC_ASSERT(static_cast<int>(IsPartiallyDependent::kShift) == STATIC_ASSERT(static_cast<int>(IsPartiallyDependent::kShift) ==
Internals::kNodeIsPartiallyDependentShift); Internals::kNodeIsPartiallyDependentShift);
STATIC_ASSERT(static_cast<int>(IsActive::kShift) ==
Internals::kNodeIsActiveShift);
} }
#ifdef ENABLE_HANDLE_ZAPPING #ifdef ENABLE_HANDLE_ZAPPING
...@@ -64,7 +66,11 @@ class GlobalHandles::Node { ...@@ -64,7 +66,11 @@ class GlobalHandles::Node {
class_id_ = v8::HeapProfiler::kPersistentHandleNoClassId; class_id_ = v8::HeapProfiler::kPersistentHandleNoClassId;
index_ = 0; index_ = 0;
set_independent(false); set_independent(false);
set_partially_dependent(false); if (FLAG_scavenge_reclaim_unmodified_objects) {
set_active(false);
} else {
set_partially_dependent(false);
}
set_in_new_space_list(false); set_in_new_space_list(false);
parameter_or_next_free_.next_free = NULL; parameter_or_next_free_.next_free = NULL;
weak_callback_ = NULL; weak_callback_ = NULL;
...@@ -86,7 +92,11 @@ class GlobalHandles::Node { ...@@ -86,7 +92,11 @@ class GlobalHandles::Node {
object_ = object; object_ = object;
class_id_ = v8::HeapProfiler::kPersistentHandleNoClassId; class_id_ = v8::HeapProfiler::kPersistentHandleNoClassId;
set_independent(false); set_independent(false);
set_partially_dependent(false); if (FLAG_scavenge_reclaim_unmodified_objects) {
set_active(false);
} else {
set_partially_dependent(false);
}
set_state(NORMAL); set_state(NORMAL);
parameter_or_next_free_.parameter = NULL; parameter_or_next_free_.parameter = NULL;
weak_callback_ = NULL; weak_callback_ = NULL;
...@@ -106,7 +116,11 @@ class GlobalHandles::Node { ...@@ -106,7 +116,11 @@ class GlobalHandles::Node {
object_ = reinterpret_cast<Object*>(kGlobalHandleZapValue); object_ = reinterpret_cast<Object*>(kGlobalHandleZapValue);
class_id_ = v8::HeapProfiler::kPersistentHandleNoClassId; class_id_ = v8::HeapProfiler::kPersistentHandleNoClassId;
set_independent(false); set_independent(false);
set_partially_dependent(false); if (FLAG_scavenge_reclaim_unmodified_objects) {
set_active(false);
} else {
set_partially_dependent(false);
}
weak_callback_ = NULL; weak_callback_ = NULL;
DecreaseBlockUses(); DecreaseBlockUses();
} }
...@@ -140,12 +154,23 @@ class GlobalHandles::Node { ...@@ -140,12 +154,23 @@ class GlobalHandles::Node {
} }
bool is_partially_dependent() { bool is_partially_dependent() {
CHECK(!FLAG_scavenge_reclaim_unmodified_objects);
return IsPartiallyDependent::decode(flags_); return IsPartiallyDependent::decode(flags_);
} }
void set_partially_dependent(bool v) { void set_partially_dependent(bool v) {
CHECK(!FLAG_scavenge_reclaim_unmodified_objects);
flags_ = IsPartiallyDependent::update(flags_, v); flags_ = IsPartiallyDependent::update(flags_, v);
} }
bool is_active() {
CHECK(FLAG_scavenge_reclaim_unmodified_objects);
return IsActive::decode(flags_);
}
void set_active(bool v) {
CHECK(FLAG_scavenge_reclaim_unmodified_objects);
flags_ = IsActive::update(flags_, v);
}
bool is_in_new_space_list() { bool is_in_new_space_list() {
return IsInNewSpaceList::decode(flags_); return IsInNewSpaceList::decode(flags_);
} }
...@@ -349,6 +374,8 @@ class GlobalHandles::Node { ...@@ -349,6 +374,8 @@ class GlobalHandles::Node {
// in_new_space_list) and a State. // in_new_space_list) and a State.
class NodeState : public BitField<State, 0, 3> {}; class NodeState : public BitField<State, 0, 3> {};
class IsIndependent : public BitField<bool, 3, 1> {}; class IsIndependent : public BitField<bool, 3, 1> {};
// The following two fields are mutually exclusive
class IsActive : public BitField<bool, 4, 1> {};
class IsPartiallyDependent : public BitField<bool, 4, 1> {}; class IsPartiallyDependent : public BitField<bool, 4, 1> {};
class IsInNewSpaceList : public BitField<bool, 5, 1> {}; class IsInNewSpaceList : public BitField<bool, 5, 1> {};
class NodeWeaknessType : public BitField<WeaknessType, 6, 2> {}; class NodeWeaknessType : public BitField<WeaknessType, 6, 2> {};
...@@ -646,10 +673,18 @@ void GlobalHandles::IdentifyWeakHandles(WeakSlotCallback f) { ...@@ -646,10 +673,18 @@ void GlobalHandles::IdentifyWeakHandles(WeakSlotCallback f) {
void GlobalHandles::IterateNewSpaceStrongAndDependentRoots(ObjectVisitor* v) { void GlobalHandles::IterateNewSpaceStrongAndDependentRoots(ObjectVisitor* v) {
for (int i = 0; i < new_space_nodes_.length(); ++i) { for (int i = 0; i < new_space_nodes_.length(); ++i) {
Node* node = new_space_nodes_[i]; Node* node = new_space_nodes_[i];
if (node->IsStrongRetainer() || if (FLAG_scavenge_reclaim_unmodified_objects) {
(node->IsWeakRetainer() && !node->is_independent() && if (node->IsStrongRetainer() ||
!node->is_partially_dependent())) { (node->IsWeakRetainer() && !node->is_independent() &&
node->is_active())) {
v->VisitPointer(node->location());
}
} else {
if (node->IsStrongRetainer() ||
(node->IsWeakRetainer() && !node->is_independent() &&
!node->is_partially_dependent())) {
v->VisitPointer(node->location()); v->VisitPointer(node->location());
}
} }
} }
} }
...@@ -687,6 +722,49 @@ void GlobalHandles::IterateNewSpaceWeakIndependentRoots(ObjectVisitor* v) { ...@@ -687,6 +722,49 @@ void GlobalHandles::IterateNewSpaceWeakIndependentRoots(ObjectVisitor* v) {
} }
void GlobalHandles::IdentifyWeakUnmodifiedObjects(
WeakSlotCallback is_unmodified) {
for (int i = 0; i < new_space_nodes_.length(); ++i) {
Node* node = new_space_nodes_[i];
if (node->IsWeak() && !is_unmodified(node->location())) {
node->set_active(true);
}
}
}
void GlobalHandles::MarkNewSpaceWeakUnmodifiedObjectsPending(
WeakSlotCallbackWithHeap is_unscavenged) {
for (int i = 0; i < new_space_nodes_.length(); ++i) {
Node* node = new_space_nodes_[i];
DCHECK(node->is_in_new_space_list());
if ((node->is_independent() || !node->is_active()) && node->IsWeak() &&
is_unscavenged(isolate_->heap(), node->location())) {
node->MarkPending();
}
}
}
void GlobalHandles::IterateNewSpaceWeakUnmodifiedRoots(ObjectVisitor* v) {
for (int i = 0; i < new_space_nodes_.length(); ++i) {
Node* node = new_space_nodes_[i];
DCHECK(node->is_in_new_space_list());
if ((node->is_independent() || !node->is_active()) &&
node->IsWeakRetainer()) {
// Pending weak phantom handles die immediately. Everything else survives.
if (node->state() == Node::PENDING &&
node->weakness_type() != NORMAL_WEAK) {
node->CollectPhantomCallbackData(isolate(),
&pending_phantom_callbacks_);
} else {
v->VisitPointer(node->location());
}
}
}
}
bool GlobalHandles::IterateObjectGroups(ObjectVisitor* v, bool GlobalHandles::IterateObjectGroups(ObjectVisitor* v,
WeakSlotCallbackWithHeap can_skip) { WeakSlotCallbackWithHeap can_skip) {
ComputeObjectGroupsAndImplicitReferences(); ComputeObjectGroupsAndImplicitReferences();
...@@ -757,13 +835,23 @@ int GlobalHandles::PostScavengeProcessing( ...@@ -757,13 +835,23 @@ int GlobalHandles::PostScavengeProcessing(
// the freed_nodes. // the freed_nodes.
continue; continue;
} }
// Skip dependent handles. Their weak callbacks might expect to be // Skip dependent or unmodified handles. Their weak callbacks might expect
// to be
// called between two global garbage collection callbacks which // called between two global garbage collection callbacks which
// are not called for minor collections. // are not called for minor collections.
if (!node->is_independent() && !node->is_partially_dependent()) { if (FLAG_scavenge_reclaim_unmodified_objects) {
continue; if (!node->is_independent() && (node->is_active())) {
node->set_active(false);
continue;
}
node->set_active(false);
} else {
if (!node->is_independent() && !node->is_partially_dependent()) {
continue;
}
node->clear_partially_dependent();
} }
node->clear_partially_dependent();
if (node->PostGarbageCollectionProcessing(isolate_)) { if (node->PostGarbageCollectionProcessing(isolate_)) {
if (initial_post_gc_processing_count != post_gc_processing_count_) { if (initial_post_gc_processing_count != post_gc_processing_count_) {
// Weak callback triggered another GC and another round of // Weak callback triggered another GC and another round of
...@@ -790,7 +878,11 @@ int GlobalHandles::PostMarkSweepProcessing( ...@@ -790,7 +878,11 @@ int GlobalHandles::PostMarkSweepProcessing(
// the freed_nodes. // the freed_nodes.
continue; continue;
} }
it.node()->clear_partially_dependent(); if (FLAG_scavenge_reclaim_unmodified_objects) {
it.node()->set_active(false);
} else {
it.node()->clear_partially_dependent();
}
if (it.node()->PostGarbageCollectionProcessing(isolate_)) { if (it.node()->PostGarbageCollectionProcessing(isolate_)) {
if (initial_post_gc_processing_count != post_gc_processing_count_) { if (initial_post_gc_processing_count != post_gc_processing_count_) {
// See the comment above. // See the comment above.
...@@ -955,6 +1047,16 @@ void GlobalHandles::IterateAllRootsInNewSpaceWithClassIds(ObjectVisitor* v) { ...@@ -955,6 +1047,16 @@ void GlobalHandles::IterateAllRootsInNewSpaceWithClassIds(ObjectVisitor* v) {
} }
void GlobalHandles::IterateWeakRootsInNewSpaceWithClassIds(ObjectVisitor* v) {
for (int i = 0; i < new_space_nodes_.length(); ++i) {
Node* node = new_space_nodes_[i];
if (node->has_wrapper_class_id() && node->IsWeak()) {
v->VisitEmbedderReference(node->location(), node->wrapper_class_id());
}
}
}
int GlobalHandles::NumberOfWeakHandles() { int GlobalHandles::NumberOfWeakHandles() {
int count = 0; int count = 0;
for (NodeIterator it(this); !it.done(); it.Advance()) { for (NodeIterator it(this); !it.done(); it.Advance()) {
......
...@@ -197,6 +197,10 @@ class GlobalHandles { ...@@ -197,6 +197,10 @@ class GlobalHandles {
// class ID. // class ID.
void IterateAllRootsInNewSpaceWithClassIds(ObjectVisitor* v); void IterateAllRootsInNewSpaceWithClassIds(ObjectVisitor* v);
// Iterate over all handles in the new space that are weak, unmodified
// and have class IDs
void IterateWeakRootsInNewSpaceWithClassIds(ObjectVisitor* v);
// Iterates over all weak roots in heap. // Iterates over all weak roots in heap.
void IterateWeakRoots(ObjectVisitor* v); void IterateWeakRoots(ObjectVisitor* v);
...@@ -204,7 +208,7 @@ class GlobalHandles { ...@@ -204,7 +208,7 @@ class GlobalHandles {
// them as pending. // them as pending.
void IdentifyWeakHandles(WeakSlotCallback f); void IdentifyWeakHandles(WeakSlotCallback f);
// NOTE: Three ...NewSpace... functions below are used during // NOTE: Five ...NewSpace... functions below are used during
// scavenge collections and iterate over sets of handles that are // scavenge collections and iterate over sets of handles that are
// guaranteed to contain all handles holding new space objects (but // guaranteed to contain all handles holding new space objects (but
// may also include old space objects). // may also include old space objects).
...@@ -220,6 +224,19 @@ class GlobalHandles { ...@@ -220,6 +224,19 @@ class GlobalHandles {
// See the note above. // See the note above.
void IterateNewSpaceWeakIndependentRoots(ObjectVisitor* v); void IterateNewSpaceWeakIndependentRoots(ObjectVisitor* v);
// Finds weak independent or unmodified handles satisfying
// the callback predicate and marks them as pending. See the note above.
void MarkNewSpaceWeakUnmodifiedObjectsPending(
WeakSlotCallbackWithHeap is_unscavenged);
// Iterates over weak independent or unmodified handles.
// See the note above.
void IterateNewSpaceWeakUnmodifiedRoots(ObjectVisitor* v);
// Identify unmodified objects that are in weak state and marks them
// unmodified
void IdentifyWeakUnmodifiedObjects(WeakSlotCallback is_unmodified);
// Iterate over objects in object groups that have at least one object // Iterate over objects in object groups that have at least one object
// which requires visiting. The callback has to return true if objects // which requires visiting. The callback has to return true if objects
// can be skipped and false otherwise. // can be skipped and false otherwise.
......
...@@ -1501,6 +1501,22 @@ static bool IsUnscavengedHeapObject(Heap* heap, Object** p) { ...@@ -1501,6 +1501,22 @@ static bool IsUnscavengedHeapObject(Heap* heap, Object** p) {
} }
static bool IsUnmodifiedHeapObject(Object** p) {
Object* object = *p;
DCHECK(object->IsHeapObject());
HeapObject* heap_object = HeapObject::cast(object);
if (!object->IsJSObject()) return false;
Object* obj_constructor = (JSObject::cast(object))->map()->GetConstructor();
if (!obj_constructor->IsJSFunction()) return false;
JSFunction* constructor = JSFunction::cast(obj_constructor);
if (constructor != nullptr &&
constructor->initial_map() == heap_object->map()) {
return true;
}
return false;
}
void Heap::ScavengeStoreBufferCallback(Heap* heap, MemoryChunk* page, void Heap::ScavengeStoreBufferCallback(Heap* heap, MemoryChunk* page,
StoreBufferEvent event) { StoreBufferEvent event) {
heap->store_buffer_rebuilder_.Callback(page, event); heap->store_buffer_rebuilder_.Callback(page, event);
...@@ -1619,6 +1635,12 @@ void Heap::Scavenge() { ...@@ -1619,6 +1635,12 @@ void Heap::Scavenge() {
promotion_queue_.Initialize(); promotion_queue_.Initialize();
ScavengeVisitor scavenge_visitor(this); ScavengeVisitor scavenge_visitor(this);
if (FLAG_scavenge_reclaim_unmodified_objects) {
isolate()->global_handles()->IdentifyWeakUnmodifiedObjects(
&IsUnmodifiedHeapObject);
}
{ {
// Copy roots. // Copy roots.
GCTracer::Scope gc_scope(tracer(), GCTracer::Scope::SCAVENGER_ROOTS); GCTracer::Scope gc_scope(tracer(), GCTracer::Scope::SCAVENGER_ROOTS);
...@@ -1657,7 +1679,14 @@ void Heap::Scavenge() { ...@@ -1657,7 +1679,14 @@ void Heap::Scavenge() {
new_space_front = DoScavenge(&scavenge_visitor, new_space_front); new_space_front = DoScavenge(&scavenge_visitor, new_space_front);
} }
{ if (FLAG_scavenge_reclaim_unmodified_objects) {
isolate()->global_handles()->MarkNewSpaceWeakUnmodifiedObjectsPending(
&IsUnscavengedHeapObject);
isolate()->global_handles()->IterateNewSpaceWeakUnmodifiedRoots(
&scavenge_visitor);
new_space_front = DoScavenge(&scavenge_visitor, new_space_front);
} else {
GCTracer::Scope gc_scope(tracer(), GCTracer::Scope gc_scope(tracer(),
GCTracer::Scope::SCAVENGER_OBJECT_GROUPS); GCTracer::Scope::SCAVENGER_OBJECT_GROUPS);
while (isolate()->global_handles()->IterateObjectGroups( while (isolate()->global_handles()->IterateObjectGroups(
...@@ -1666,14 +1695,14 @@ void Heap::Scavenge() { ...@@ -1666,14 +1695,14 @@ void Heap::Scavenge() {
} }
isolate()->global_handles()->RemoveObjectGroups(); isolate()->global_handles()->RemoveObjectGroups();
isolate()->global_handles()->RemoveImplicitRefGroups(); isolate()->global_handles()->RemoveImplicitRefGroups();
}
isolate()->global_handles()->IdentifyNewSpaceWeakIndependentHandles( isolate()->global_handles()->IdentifyNewSpaceWeakIndependentHandles(
&IsUnscavengedHeapObject); &IsUnscavengedHeapObject);
isolate()->global_handles()->IterateNewSpaceWeakIndependentRoots( isolate()->global_handles()->IterateNewSpaceWeakIndependentRoots(
&scavenge_visitor); &scavenge_visitor);
new_space_front = DoScavenge(&scavenge_visitor, new_space_front); new_space_front = DoScavenge(&scavenge_visitor, new_space_front);
}
UpdateNewSpaceReferencesInExternalStringTable( UpdateNewSpaceReferencesInExternalStringTable(
&UpdateNewSpaceReferenceInExternalStringTableEntry); &UpdateNewSpaceReferenceInExternalStringTableEntry);
......
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