Commit a6da98d8 authored by ulan's avatar ulan Committed by Commit bot

Introduce a new phantom weakness type without finalization callback.

Handles of this type are automatically reset by the garbage collector
when their objects are not longer reachable.

The motivation is to reduce pause time of external.weak_global_handles
phase of the garbage collector by not maintaing the list of pending
callbacks and not calling the callbacks.

Local testing on discourse page of the v8.inifinite_scroll benchmark
shows 7x improvement for this GC phase.

Before:
external.weak_global_handles
 len: 21
 min: 0.0
 max: 4.5
 avg: 0.757142857143

After:
external.weak_global_handles
 len: 21
 min: 0.0
 max: 0.5
 avg: 0.109523809524

A follow-up patch will enable the new phantom handles in Chromium.

BUG=chromium:608333
LOG=NO

Review-Url: https://codereview.chromium.org/1950963002
Cr-Commit-Position: refs/heads/master@{#36095}
parent 80a8c3f5
...@@ -545,6 +545,15 @@ template <class T> class PersistentBase { ...@@ -545,6 +545,15 @@ template <class T> class PersistentBase {
typename WeakCallbackInfo<P>::Callback callback, typename WeakCallbackInfo<P>::Callback callback,
WeakCallbackType type); WeakCallbackType type);
/**
* Turns this handle into a weak phantom handle without finalization callback.
* The handle will be reset automatically when the garbage collector detects
* that the object is no longer reachable.
* A related function Isolate::NumberOfPhantomHandleResetsSinceLastCall
* returns how many phantom handles were reset by the garbage collector.
*/
V8_INLINE void SetWeak();
template<typename P> template<typename P>
V8_INLINE P* ClearWeak(); V8_INLINE P* ClearWeak();
...@@ -5783,6 +5792,12 @@ class V8_EXPORT Isolate { ...@@ -5783,6 +5792,12 @@ class V8_EXPORT Isolate {
V8_INLINE int64_t V8_INLINE int64_t
AdjustAmountOfExternalAllocatedMemory(int64_t change_in_bytes); AdjustAmountOfExternalAllocatedMemory(int64_t change_in_bytes);
/**
* Returns the number of phantom handles without callbacks that were reset
* by the garbage collector since the last call to this function.
*/
size_t NumberOfPhantomHandleResetsSinceLastCall();
/** /**
* Returns heap profiler for this isolate. Will return NULL until the isolate * Returns heap profiler for this isolate. Will return NULL until the isolate
* is initialized. * is initialized.
...@@ -6628,16 +6643,17 @@ class V8_EXPORT V8 { ...@@ -6628,16 +6643,17 @@ class V8_EXPORT V8 {
internal::Object** handle); internal::Object** handle);
static internal::Object** CopyPersistent(internal::Object** handle); static internal::Object** CopyPersistent(internal::Object** handle);
static void DisposeGlobal(internal::Object** global_handle); static void DisposeGlobal(internal::Object** global_handle);
static void MakeWeak(internal::Object** global_handle, void* data, static void MakeWeak(internal::Object** location, void* data,
WeakCallbackInfo<void>::Callback weak_callback, WeakCallbackInfo<void>::Callback weak_callback,
WeakCallbackType type); WeakCallbackType type);
static void MakeWeak(internal::Object** global_handle, void* data, static void MakeWeak(internal::Object** location, void* data,
// Must be 0 or -1. // Must be 0 or -1.
int internal_field_index1, int internal_field_index1,
// Must be 1 or -1. // Must be 1 or -1.
int internal_field_index2, int internal_field_index2,
WeakCallbackInfo<void>::Callback weak_callback); WeakCallbackInfo<void>::Callback weak_callback);
static void* ClearWeak(internal::Object** global_handle); static void MakeWeak(internal::Object*** location_addr);
static void* ClearWeak(internal::Object** location);
static void Eternalize(Isolate* isolate, static void Eternalize(Isolate* isolate,
Value* handle, Value* handle,
int* index); int* index);
...@@ -7609,6 +7625,10 @@ V8_INLINE void PersistentBase<T>::SetWeak( ...@@ -7609,6 +7625,10 @@ V8_INLINE void PersistentBase<T>::SetWeak(
reinterpret_cast<Callback>(callback), type); reinterpret_cast<Callback>(callback), type);
} }
template <class T>
void PersistentBase<T>::SetWeak() {
V8::MakeWeak(reinterpret_cast<internal::Object***>(&this->val_));
}
template <class T> template <class T>
template <typename P> template <typename P>
......
...@@ -683,8 +683,7 @@ void V8::RegisterExternallyReferencedObject(i::Object** object, ...@@ -683,8 +683,7 @@ void V8::RegisterExternallyReferencedObject(i::Object** object,
isolate->heap()->RegisterExternallyReferencedObject(object); isolate->heap()->RegisterExternallyReferencedObject(object);
} }
void V8::MakeWeak(i::Object** location, void* parameter,
void V8::MakeWeak(i::Object** object, void* parameter,
int internal_field_index1, int internal_field_index2, int internal_field_index1, int internal_field_index2,
WeakCallbackInfo<void>::Callback weak_callback) { WeakCallbackInfo<void>::Callback weak_callback) {
WeakCallbackType type = WeakCallbackType::kParameter; WeakCallbackType type = WeakCallbackType::kParameter;
...@@ -699,24 +698,25 @@ void V8::MakeWeak(i::Object** object, void* parameter, ...@@ -699,24 +698,25 @@ void V8::MakeWeak(i::Object** object, void* parameter,
DCHECK_EQ(internal_field_index1, -1); DCHECK_EQ(internal_field_index1, -1);
DCHECK_EQ(internal_field_index2, -1); DCHECK_EQ(internal_field_index2, -1);
} }
i::GlobalHandles::MakeWeak(object, parameter, weak_callback, type); i::GlobalHandles::MakeWeak(location, parameter, weak_callback, type);
} }
void V8::MakeWeak(i::Object** location, void* parameter,
void V8::MakeWeak(i::Object** object, void* parameter,
WeakCallbackInfo<void>::Callback weak_callback, WeakCallbackInfo<void>::Callback weak_callback,
WeakCallbackType type) { WeakCallbackType type) {
i::GlobalHandles::MakeWeak(object, parameter, weak_callback, type); i::GlobalHandles::MakeWeak(location, parameter, weak_callback, type);
} }
void V8::MakeWeak(i::Object*** location_addr) {
void* V8::ClearWeak(i::Object** obj) { i::GlobalHandles::MakeWeak(location_addr);
return i::GlobalHandles::ClearWeakness(obj);
} }
void* V8::ClearWeak(i::Object** location) {
return i::GlobalHandles::ClearWeakness(location);
}
void V8::DisposeGlobal(i::Object** obj) { void V8::DisposeGlobal(i::Object** location) {
i::GlobalHandles::Destroy(obj); i::GlobalHandles::Destroy(location);
} }
...@@ -7487,6 +7487,12 @@ void Isolate::GetStackSample(const RegisterState& state, void** frames, ...@@ -7487,6 +7487,12 @@ void Isolate::GetStackSample(const RegisterState& state, void** frames,
frames, frames_limit, sample_info); frames, frames_limit, sample_info);
} }
size_t Isolate::NumberOfPhantomHandleResetsSinceLastCall() {
i::Isolate* isolate = reinterpret_cast<i::Isolate*>(this);
size_t result = isolate->global_handles()->NumberOfPhantomHandleResets();
isolate->global_handles()->ResetNumberOfPhantomHandleResets();
return result;
}
void Isolate::SetEventLogger(LogEventCallback that) { void Isolate::SetEventLogger(LogEventCallback that) {
// Do not overwrite the event logger if we want to log explicitly. // Do not overwrite the event logger if we want to log explicitly.
......
...@@ -194,6 +194,16 @@ class GlobalHandles::Node { ...@@ -194,6 +194,16 @@ class GlobalHandles::Node {
bool IsInUse() const { return state() != FREE; } bool IsInUse() const { return state() != FREE; }
bool IsPendingPhantomCallback() const {
return state() == PENDING &&
(weakness_type() == PHANTOM_WEAK ||
weakness_type() == PHANTOM_WEAK_2_INTERNAL_FIELDS);
}
bool IsPendingPhantomResetHandle() const {
return state() == PENDING && weakness_type() == PHANTOM_WEAK_RESET_HANDLE;
}
bool IsRetainer() const { bool IsRetainer() const {
return state() != FREE && return state() != FREE &&
!(state() == NEAR_DEATH && weakness_type() != FINALIZER_WEAK); !(state() == NEAR_DEATH && weakness_type() != FINALIZER_WEAK);
...@@ -271,6 +281,15 @@ class GlobalHandles::Node { ...@@ -271,6 +281,15 @@ class GlobalHandles::Node {
weak_callback_ = phantom_callback; weak_callback_ = phantom_callback;
} }
void MakeWeak(Object*** location_addr) {
DCHECK(IsInUse());
CHECK_NE(object_, reinterpret_cast<Object*>(kGlobalHandleZapValue));
set_state(WEAK);
set_weakness_type(PHANTOM_WEAK_RESET_HANDLE);
set_parameter(location_addr);
weak_callback_ = nullptr;
}
void* ClearWeakness() { void* ClearWeakness() {
DCHECK(IsInUse()); DCHECK(IsInUse());
void* p = parameter(); void* p = parameter();
...@@ -285,6 +304,7 @@ class GlobalHandles::Node { ...@@ -285,6 +304,7 @@ class GlobalHandles::Node {
DCHECK(weakness_type() == PHANTOM_WEAK || DCHECK(weakness_type() == PHANTOM_WEAK ||
weakness_type() == PHANTOM_WEAK_2_INTERNAL_FIELDS); weakness_type() == PHANTOM_WEAK_2_INTERNAL_FIELDS);
DCHECK(state() == PENDING); DCHECK(state() == PENDING);
DCHECK(weak_callback_ != nullptr);
void* internal_fields[v8::kInternalFieldsInWeakCallback] = {nullptr, void* internal_fields[v8::kInternalFieldsInWeakCallback] = {nullptr,
nullptr}; nullptr};
...@@ -309,6 +329,15 @@ class GlobalHandles::Node { ...@@ -309,6 +329,15 @@ class GlobalHandles::Node {
set_state(NEAR_DEATH); set_state(NEAR_DEATH);
} }
void ResetPhantomHandle() {
DCHECK(weakness_type() == PHANTOM_WEAK_RESET_HANDLE);
DCHECK(state() == PENDING);
DCHECK(weak_callback_ == nullptr);
Object*** handle = reinterpret_cast<Object***>(parameter());
*handle = nullptr;
Release();
}
bool PostGarbageCollectionProcessing(Isolate* isolate) { bool PostGarbageCollectionProcessing(Isolate* isolate) {
// Handles only weak handles (not phantom) that are dying. // Handles only weak handles (not phantom) that are dying.
if (state() != Node::PENDING) return false; if (state() != Node::PENDING) return false;
...@@ -540,7 +569,6 @@ class GlobalHandles::PendingPhantomCallbacksSecondPassTask ...@@ -540,7 +569,6 @@ class GlobalHandles::PendingPhantomCallbacksSecondPassTask
DISALLOW_COPY_AND_ASSIGN(PendingPhantomCallbacksSecondPassTask); DISALLOW_COPY_AND_ASSIGN(PendingPhantomCallbacksSecondPassTask);
}; };
GlobalHandles::GlobalHandles(Isolate* isolate) GlobalHandles::GlobalHandles(Isolate* isolate)
: isolate_(isolate), : isolate_(isolate),
number_of_global_handles_(0), number_of_global_handles_(0),
...@@ -548,9 +576,9 @@ GlobalHandles::GlobalHandles(Isolate* isolate) ...@@ -548,9 +576,9 @@ GlobalHandles::GlobalHandles(Isolate* isolate)
first_used_block_(NULL), first_used_block_(NULL),
first_free_(NULL), first_free_(NULL),
post_gc_processing_count_(0), post_gc_processing_count_(0),
number_of_phantom_handle_resets_(0),
object_group_connections_(kObjectGroupConnectionsCapacity) {} object_group_connections_(kObjectGroupConnectionsCapacity) {}
GlobalHandles::~GlobalHandles() { GlobalHandles::~GlobalHandles() {
NodeBlock* block = first_block_; NodeBlock* block = first_block_;
while (block != NULL) { while (block != NULL) {
...@@ -601,6 +629,9 @@ void GlobalHandles::MakeWeak(Object** location, void* parameter, ...@@ -601,6 +629,9 @@ void GlobalHandles::MakeWeak(Object** location, void* parameter,
Node::FromLocation(location)->MakeWeak(parameter, phantom_callback, type); Node::FromLocation(location)->MakeWeak(parameter, phantom_callback, type);
} }
void GlobalHandles::MakeWeak(Object*** location_addr) {
Node::FromLocation(*location_addr)->MakeWeak(location_addr);
}
void* GlobalHandles::ClearWeakness(Object** location) { void* GlobalHandles::ClearWeakness(Object** location) {
return Node::FromLocation(location)->ClearWeakness(); return Node::FromLocation(location)->ClearWeakness();
...@@ -636,8 +667,10 @@ void GlobalHandles::IterateWeakRoots(ObjectVisitor* v) { ...@@ -636,8 +667,10 @@ void GlobalHandles::IterateWeakRoots(ObjectVisitor* v) {
Node* node = it.node(); Node* node = it.node();
if (node->IsWeakRetainer()) { if (node->IsWeakRetainer()) {
// Pending weak phantom handles die immediately. Everything else survives. // Pending weak phantom handles die immediately. Everything else survives.
if (node->state() == Node::PENDING && if (node->IsPendingPhantomResetHandle()) {
node->weakness_type() != FINALIZER_WEAK) { node->ResetPhantomHandle();
++number_of_phantom_handle_resets_;
} else if (node->IsPendingPhantomCallback()) {
node->CollectPhantomCallbackData(isolate(), node->CollectPhantomCallbackData(isolate(),
&pending_phantom_callbacks_); &pending_phantom_callbacks_);
} else { } else {
...@@ -697,8 +730,10 @@ void GlobalHandles::IterateNewSpaceWeakIndependentRoots(ObjectVisitor* v) { ...@@ -697,8 +730,10 @@ void GlobalHandles::IterateNewSpaceWeakIndependentRoots(ObjectVisitor* v) {
if ((node->is_independent() || node->is_partially_dependent()) && if ((node->is_independent() || node->is_partially_dependent()) &&
node->IsWeakRetainer()) { node->IsWeakRetainer()) {
// Pending weak phantom handles die immediately. Everything else survives. // Pending weak phantom handles die immediately. Everything else survives.
if (node->state() == Node::PENDING && if (node->IsPendingPhantomResetHandle()) {
node->weakness_type() != FINALIZER_WEAK) { node->ResetPhantomHandle();
++number_of_phantom_handle_resets_;
} else if (node->IsPendingPhantomCallback()) {
node->CollectPhantomCallbackData(isolate(), node->CollectPhantomCallbackData(isolate(),
&pending_phantom_callbacks_); &pending_phantom_callbacks_);
} else { } else {
...@@ -740,8 +775,10 @@ void GlobalHandles::IterateNewSpaceWeakUnmodifiedRoots(ObjectVisitor* v) { ...@@ -740,8 +775,10 @@ void GlobalHandles::IterateNewSpaceWeakUnmodifiedRoots(ObjectVisitor* v) {
if ((node->is_independent() || !node->is_active()) && if ((node->is_independent() || !node->is_active()) &&
node->IsWeakRetainer()) { node->IsWeakRetainer()) {
// Pending weak phantom handles die immediately. Everything else survives. // Pending weak phantom handles die immediately. Everything else survives.
if (node->state() == Node::PENDING && if (node->IsPendingPhantomResetHandle()) {
node->weakness_type() != FINALIZER_WEAK) { node->ResetPhantomHandle();
++number_of_phantom_handle_resets_;
} else if (node->IsPendingPhantomCallback()) {
node->CollectPhantomCallbackData(isolate(), node->CollectPhantomCallbackData(isolate(),
&pending_phantom_callbacks_); &pending_phantom_callbacks_);
} else { } else {
......
...@@ -97,6 +97,8 @@ struct ObjectGroupRetainerInfo { ...@@ -97,6 +97,8 @@ struct ObjectGroupRetainerInfo {
}; };
enum WeaknessType { enum WeaknessType {
// Embedder gets a handle to the dying object.
FINALIZER_WEAK,
// In the following cases, the embedder gets the parameter they passed in // In the following cases, the embedder gets the parameter they passed in
// earlier, and 0 or 2 first internal fields. Note that the internal // earlier, and 0 or 2 first internal fields. Note that the internal
// fields must contain aligned non-V8 pointers. Getting pointers to V8 // fields must contain aligned non-V8 pointers. Getting pointers to V8
...@@ -104,8 +106,9 @@ enum WeaknessType { ...@@ -104,8 +106,9 @@ enum WeaknessType {
// embedder gets a null pointer instead. // embedder gets a null pointer instead.
PHANTOM_WEAK, PHANTOM_WEAK,
PHANTOM_WEAK_2_INTERNAL_FIELDS, PHANTOM_WEAK_2_INTERNAL_FIELDS,
// Embedder gets a handle to the dying object. // The handle is automatically reset by the garbage collector when
FINALIZER_WEAK, // the object is no longer reachable.
PHANTOM_WEAK_RESET_HANDLE
}; };
class GlobalHandles { class GlobalHandles {
...@@ -134,6 +137,8 @@ class GlobalHandles { ...@@ -134,6 +137,8 @@ class GlobalHandles {
WeakCallbackInfo<void>::Callback weak_callback, WeakCallbackInfo<void>::Callback weak_callback,
v8::WeakCallbackType type); v8::WeakCallbackType type);
static void MakeWeak(Object*** location_addr);
void RecordStats(HeapStats* stats); void RecordStats(HeapStats* stats);
// Returns the current number of weak handles. // Returns the current number of weak handles.
...@@ -148,6 +153,14 @@ class GlobalHandles { ...@@ -148,6 +153,14 @@ class GlobalHandles {
return number_of_global_handles_; return number_of_global_handles_;
} }
size_t NumberOfPhantomHandleResets() {
return number_of_phantom_handle_resets_;
}
void ResetNumberOfPhantomHandleResets() {
number_of_phantom_handle_resets_ = 0;
}
// Clear the weakness of a global handle. // Clear the weakness of a global handle.
static void* ClearWeakness(Object** location); static void* ClearWeakness(Object** location);
...@@ -330,6 +343,8 @@ class GlobalHandles { ...@@ -330,6 +343,8 @@ class GlobalHandles {
int post_gc_processing_count_; int post_gc_processing_count_;
size_t number_of_phantom_handle_resets_;
// Object groups and implicit references, public and more efficient // Object groups and implicit references, public and more efficient
// representation. // representation.
List<ObjectGroup*> object_groups_; List<ObjectGroup*> object_groups_;
......
...@@ -450,3 +450,22 @@ TEST(FinalizerWeakness) { ...@@ -450,3 +450,22 @@ TEST(FinalizerWeakness) {
CHECK_EQ(identity, o->GetIdentityHash()); CHECK_EQ(identity, o->GetIdentityHash());
CHECK(o->Has(isolate->GetCurrentContext(), v8_str("finalizer")).FromJust()); CHECK(o->Has(isolate->GetCurrentContext(), v8_str("finalizer")).FromJust());
} }
TEST(PhatomHandlesWithoutCallbacks) {
CcTest::InitializeVM();
v8::Isolate* isolate = CcTest::isolate();
v8::Global<v8::Object> g1, g2;
{
v8::HandleScope scope(isolate);
g1.Reset(isolate, v8::Object::New(isolate));
g1.SetWeak();
g2.Reset(isolate, v8::Object::New(isolate));
g2.SetWeak();
}
CHECK_EQ(0, isolate->NumberOfPhantomHandleResetsSinceLastCall());
CcTest::i_isolate()->heap()->CollectAllAvailableGarbage();
CHECK_EQ(2, isolate->NumberOfPhantomHandleResetsSinceLastCall());
CHECK_EQ(0, isolate->NumberOfPhantomHandleResetsSinceLastCall());
}
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