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 {
typename WeakCallbackInfo<P>::Callback callback,
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>
V8_INLINE P* ClearWeak();
......@@ -5783,6 +5792,12 @@ class V8_EXPORT Isolate {
V8_INLINE int64_t
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
* is initialized.
......@@ -6628,16 +6643,17 @@ class V8_EXPORT V8 {
internal::Object** handle);
static internal::Object** CopyPersistent(internal::Object** 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,
WeakCallbackType type);
static void MakeWeak(internal::Object** global_handle, void* data,
static void MakeWeak(internal::Object** location, void* data,
// Must be 0 or -1.
int internal_field_index1,
// Must be 1 or -1.
int internal_field_index2,
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,
Value* handle,
int* index);
......@@ -7609,6 +7625,10 @@ V8_INLINE void PersistentBase<T>::SetWeak(
reinterpret_cast<Callback>(callback), type);
}
template <class T>
void PersistentBase<T>::SetWeak() {
V8::MakeWeak(reinterpret_cast<internal::Object***>(&this->val_));
}
template <class T>
template <typename P>
......
......@@ -683,8 +683,7 @@ void V8::RegisterExternallyReferencedObject(i::Object** object,
isolate->heap()->RegisterExternallyReferencedObject(object);
}
void V8::MakeWeak(i::Object** object, void* parameter,
void V8::MakeWeak(i::Object** location, void* parameter,
int internal_field_index1, int internal_field_index2,
WeakCallbackInfo<void>::Callback weak_callback) {
WeakCallbackType type = WeakCallbackType::kParameter;
......@@ -699,24 +698,25 @@ void V8::MakeWeak(i::Object** object, void* parameter,
DCHECK_EQ(internal_field_index1, -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** object, void* parameter,
void V8::MakeWeak(i::Object** location, void* parameter,
WeakCallbackInfo<void>::Callback weak_callback,
WeakCallbackType type) {
i::GlobalHandles::MakeWeak(object, parameter, weak_callback, type);
i::GlobalHandles::MakeWeak(location, parameter, weak_callback, type);
}
void* V8::ClearWeak(i::Object** obj) {
return i::GlobalHandles::ClearWeakness(obj);
void V8::MakeWeak(i::Object*** location_addr) {
i::GlobalHandles::MakeWeak(location_addr);
}
void* V8::ClearWeak(i::Object** location) {
return i::GlobalHandles::ClearWeakness(location);
}
void V8::DisposeGlobal(i::Object** obj) {
i::GlobalHandles::Destroy(obj);
void V8::DisposeGlobal(i::Object** location) {
i::GlobalHandles::Destroy(location);
}
......@@ -7487,6 +7487,12 @@ void Isolate::GetStackSample(const RegisterState& state, void** frames,
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) {
// Do not overwrite the event logger if we want to log explicitly.
......
......@@ -194,6 +194,16 @@ class GlobalHandles::Node {
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 {
return state() != FREE &&
!(state() == NEAR_DEATH && weakness_type() != FINALIZER_WEAK);
......@@ -271,6 +281,15 @@ class GlobalHandles::Node {
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() {
DCHECK(IsInUse());
void* p = parameter();
......@@ -285,6 +304,7 @@ class GlobalHandles::Node {
DCHECK(weakness_type() == PHANTOM_WEAK ||
weakness_type() == PHANTOM_WEAK_2_INTERNAL_FIELDS);
DCHECK(state() == PENDING);
DCHECK(weak_callback_ != nullptr);
void* internal_fields[v8::kInternalFieldsInWeakCallback] = {nullptr,
nullptr};
......@@ -309,6 +329,15 @@ class GlobalHandles::Node {
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) {
// Handles only weak handles (not phantom) that are dying.
if (state() != Node::PENDING) return false;
......@@ -540,7 +569,6 @@ class GlobalHandles::PendingPhantomCallbacksSecondPassTask
DISALLOW_COPY_AND_ASSIGN(PendingPhantomCallbacksSecondPassTask);
};
GlobalHandles::GlobalHandles(Isolate* isolate)
: isolate_(isolate),
number_of_global_handles_(0),
......@@ -548,9 +576,9 @@ GlobalHandles::GlobalHandles(Isolate* isolate)
first_used_block_(NULL),
first_free_(NULL),
post_gc_processing_count_(0),
number_of_phantom_handle_resets_(0),
object_group_connections_(kObjectGroupConnectionsCapacity) {}
GlobalHandles::~GlobalHandles() {
NodeBlock* block = first_block_;
while (block != NULL) {
......@@ -601,6 +629,9 @@ void GlobalHandles::MakeWeak(Object** location, void* parameter,
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) {
return Node::FromLocation(location)->ClearWeakness();
......@@ -636,8 +667,10 @@ void GlobalHandles::IterateWeakRoots(ObjectVisitor* v) {
Node* node = it.node();
if (node->IsWeakRetainer()) {
// Pending weak phantom handles die immediately. Everything else survives.
if (node->state() == Node::PENDING &&
node->weakness_type() != FINALIZER_WEAK) {
if (node->IsPendingPhantomResetHandle()) {
node->ResetPhantomHandle();
++number_of_phantom_handle_resets_;
} else if (node->IsPendingPhantomCallback()) {
node->CollectPhantomCallbackData(isolate(),
&pending_phantom_callbacks_);
} else {
......@@ -697,8 +730,10 @@ void GlobalHandles::IterateNewSpaceWeakIndependentRoots(ObjectVisitor* v) {
if ((node->is_independent() || node->is_partially_dependent()) &&
node->IsWeakRetainer()) {
// Pending weak phantom handles die immediately. Everything else survives.
if (node->state() == Node::PENDING &&
node->weakness_type() != FINALIZER_WEAK) {
if (node->IsPendingPhantomResetHandle()) {
node->ResetPhantomHandle();
++number_of_phantom_handle_resets_;
} else if (node->IsPendingPhantomCallback()) {
node->CollectPhantomCallbackData(isolate(),
&pending_phantom_callbacks_);
} else {
......@@ -740,8 +775,10 @@ void GlobalHandles::IterateNewSpaceWeakUnmodifiedRoots(ObjectVisitor* v) {
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() != FINALIZER_WEAK) {
if (node->IsPendingPhantomResetHandle()) {
node->ResetPhantomHandle();
++number_of_phantom_handle_resets_;
} else if (node->IsPendingPhantomCallback()) {
node->CollectPhantomCallbackData(isolate(),
&pending_phantom_callbacks_);
} else {
......
......@@ -97,6 +97,8 @@ struct ObjectGroupRetainerInfo {
};
enum WeaknessType {
// Embedder gets a handle to the dying object.
FINALIZER_WEAK,
// In the following cases, the embedder gets the parameter they passed in
// earlier, and 0 or 2 first internal fields. Note that the internal
// fields must contain aligned non-V8 pointers. Getting pointers to V8
......@@ -104,8 +106,9 @@ enum WeaknessType {
// embedder gets a null pointer instead.
PHANTOM_WEAK,
PHANTOM_WEAK_2_INTERNAL_FIELDS,
// Embedder gets a handle to the dying object.
FINALIZER_WEAK,
// The handle is automatically reset by the garbage collector when
// the object is no longer reachable.
PHANTOM_WEAK_RESET_HANDLE
};
class GlobalHandles {
......@@ -134,6 +137,8 @@ class GlobalHandles {
WeakCallbackInfo<void>::Callback weak_callback,
v8::WeakCallbackType type);
static void MakeWeak(Object*** location_addr);
void RecordStats(HeapStats* stats);
// Returns the current number of weak handles.
......@@ -148,6 +153,14 @@ class GlobalHandles {
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.
static void* ClearWeakness(Object** location);
......@@ -330,6 +343,8 @@ class GlobalHandles {
int post_gc_processing_count_;
size_t number_of_phantom_handle_resets_;
// Object groups and implicit references, public and more efficient
// representation.
List<ObjectGroup*> object_groups_;
......
......@@ -450,3 +450,22 @@ TEST(FinalizerWeakness) {
CHECK_EQ(identity, o->GetIdentityHash());
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