New GC related APIs.

When using these APIs, the embedder doesn't need to copy Persistent handles around.

BUG=NONE

Review URL: https://codereview.chromium.org/13786002
Patch from Marja Hölttä <marja@chromium.org>.

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@14215 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
parent 46508ec2
...@@ -144,6 +144,28 @@ class Isolate; ...@@ -144,6 +144,28 @@ class Isolate;
class Object; class Object;
} }
// Generic-purpose unique identifier.
class UniqueId {
public:
explicit UniqueId(intptr_t data)
: data_(data) {}
bool operator==(const UniqueId& other) const {
return data_ == other.data_;
}
bool operator!=(const UniqueId& other) const {
return data_ != other.data_;
}
bool operator<(const UniqueId& other) const {
return data_ < other.data_;
}
private:
intptr_t data_;
};
// --- Weak Handles --- // --- Weak Handles ---
...@@ -3503,6 +3525,8 @@ class V8EXPORT V8 { ...@@ -3503,6 +3525,8 @@ class V8EXPORT V8 {
* for partially dependent handles only. * for partially dependent handles only.
* See v8-profiler.h for RetainedObjectInfo interface description. * See v8-profiler.h for RetainedObjectInfo interface description.
*/ */
// TODO(marja): deprecate AddObjectGroup. Use SetObjectGroupID and
// SetRetainedObjectInfo instead.
static void AddObjectGroup(Persistent<Value>* objects, static void AddObjectGroup(Persistent<Value>* objects,
size_t length, size_t length,
RetainedObjectInfo* info = NULL); RetainedObjectInfo* info = NULL);
...@@ -3511,6 +3535,14 @@ class V8EXPORT V8 { ...@@ -3511,6 +3535,14 @@ class V8EXPORT V8 {
size_t length, size_t length,
RetainedObjectInfo* info = NULL); RetainedObjectInfo* info = NULL);
static void SetObjectGroupId(Isolate* isolate,
const Persistent<Value>& object,
UniqueId id);
static void SetRetainedObjectInfo(Isolate* isolate,
UniqueId id,
RetainedObjectInfo* info);
/** /**
* Allows the host application to declare implicit references between * Allows the host application to declare implicit references between
* the objects: if |parent| is alive, all |children| are alive too. * the objects: if |parent| is alive, all |children| are alive too.
......
...@@ -5896,6 +5896,23 @@ void V8::AddObjectGroup(Isolate* exported_isolate, ...@@ -5896,6 +5896,23 @@ void V8::AddObjectGroup(Isolate* exported_isolate,
} }
void V8::SetObjectGroupId(Isolate* exported_isolate,
const Persistent<Value>& object,
UniqueId id) {
i::Isolate* isolate = reinterpret_cast<i::Isolate*>(exported_isolate);
isolate->global_handles()->SetObjectGroupId(
reinterpret_cast<i::Object**>(*object), id);
}
void V8::SetRetainedObjectInfo(Isolate* exported_isolate,
UniqueId id,
RetainedObjectInfo* info) {
i::Isolate* isolate = reinterpret_cast<i::Isolate*>(exported_isolate);
isolate->global_handles()->SetRetainedObjectInfo(id, info);
}
void V8::AddImplicitReferences(Persistent<Object> parent, void V8::AddImplicitReferences(Persistent<Object> parent,
Persistent<Value>* children, Persistent<Value>* children,
size_t length) { size_t length) {
......
...@@ -36,11 +36,6 @@ namespace v8 { ...@@ -36,11 +36,6 @@ namespace v8 {
namespace internal { namespace internal {
ObjectGroup::~ObjectGroup() {
if (info_ != NULL) info_->Dispose();
}
class GlobalHandles::Node { class GlobalHandles::Node {
public: public:
// State transition diagram: // State transition diagram:
...@@ -578,45 +573,77 @@ void GlobalHandles::IterateNewSpaceWeakIndependentRoots(ObjectVisitor* v) { ...@@ -578,45 +573,77 @@ void GlobalHandles::IterateNewSpaceWeakIndependentRoots(ObjectVisitor* v) {
bool GlobalHandles::IterateObjectGroups(ObjectVisitor* v, bool GlobalHandles::IterateObjectGroups(ObjectVisitor* v,
WeakSlotCallbackWithHeap can_skip) { WeakSlotCallbackWithHeap can_skip) {
int last = 0; if (object_groups_.length() == 0)
return false;
object_groups_.Sort();
retainer_infos_.Sort();
// During the iteration, some of the elements of object_groups are
// deleted. This is done by moving surviving elements at the front of the list
// and deleting from the end. This index tracks where the next surviving
// element should be moved.
int surviving_element_index = 0;
int info_index = 0; // For iterating retainer_infos_.
int surviving_info_index = 0;
UniqueId current_group_id(0);
size_t current_group_start = 0;
bool any_group_was_visited = false; bool any_group_was_visited = false;
for (int i = 0; i < object_groups_.length(); i++) {
ObjectGroup* entry = object_groups_.at(i); for (int i = 0; i <= object_groups_.length(); ++i) {
ASSERT(entry != NULL); if (i == 0)
current_group_id = object_groups_[i].id;
Object*** objects = entry->objects_; if (i == object_groups_.length() ||
bool group_should_be_visited = false; current_group_id != object_groups_[i].id) {
for (size_t j = 0; j < entry->length_; j++) { // Group detected: objects in indices [current_group_start, i[.
Object* object = *objects[j]; bool group_should_be_visited = false;
if (object->IsHeapObject()) { for (int j = current_group_start; j < i; ++j) {
if (!can_skip(isolate_->heap(), &object)) { Object* object = *(object_groups_[j].object);
group_should_be_visited = true; if (object->IsHeapObject()) {
break; if (!can_skip(isolate_->heap(), &object)) {
group_should_be_visited = true;
break;
}
} }
} }
}
if (!group_should_be_visited) { if (!group_should_be_visited) {
object_groups_[last++] = entry; for (int j = current_group_start; j < i; ++j)
continue; object_groups_[surviving_element_index++] = object_groups_[j];
} } else {
// An object in the group requires visiting, so iterate over all
// objects in the group.
for (int j = current_group_start; j < i; ++j) {
Object* object = *(object_groups_[j].object);
if (object->IsHeapObject()) {
v->VisitPointer(&object);
any_group_was_visited = true;
}
}
}
// An object in the group requires visiting, so iterate over all if (info_index < retainer_infos_.length() &&
// objects in the group. retainer_infos_[info_index].id ==
for (size_t j = 0; j < entry->length_; ++j) { object_groups_[current_group_start].id) {
Object* object = *objects[j]; // This object group has an associated ObjectGroupRetainerInfo.
if (object->IsHeapObject()) { if (!group_should_be_visited) {
v->VisitPointer(&object); retainer_infos_[surviving_info_index++] =
any_group_was_visited = true; retainer_infos_[info_index];
} else if (retainer_infos_[info_index].info != NULL) {
retainer_infos_[info_index].info->Dispose();
retainer_infos_[info_index].info = NULL;
}
++info_index;
}
if (i < object_groups_.length()) {
current_group_id = object_groups_[i].id;
current_group_start = i;
} }
} }
// Once the entire group has been iterated over, set the object
// group to NULL so it won't be processed again.
entry->Dispose();
object_groups_.at(i) = NULL;
} }
object_groups_.Rewind(last); object_groups_.Rewind(surviving_element_index);
retainer_infos_.Rewind(surviving_info_index);
return any_group_was_visited; return any_group_was_visited;
} }
...@@ -824,7 +851,23 @@ void GlobalHandles::AddObjectGroup(Object*** handles, ...@@ -824,7 +851,23 @@ void GlobalHandles::AddObjectGroup(Object*** handles,
if (info != NULL) info->Dispose(); if (info != NULL) info->Dispose();
return; return;
} }
object_groups_.Add(ObjectGroup::New(handles, length, info)); for (size_t i = 0; i < length; ++i) {
object_groups_.Add(ObjectGroupConnection(
UniqueId(reinterpret_cast<intptr_t>(handles[0])), handles[i]));
}
retainer_infos_.Add(ObjectGroupRetainerInfo(
UniqueId(reinterpret_cast<intptr_t>(handles[0])), info));
}
void GlobalHandles::SetObjectGroupId(Object** handle,
UniqueId id) {
object_groups_.Add(ObjectGroupConnection(id, handle));
}
void GlobalHandles::SetRetainedObjectInfo(UniqueId id,
RetainedObjectInfo* info) {
retainer_infos_.Add(ObjectGroupRetainerInfo(id, info));
} }
...@@ -843,10 +886,12 @@ void GlobalHandles::AddImplicitReferences(HeapObject** parent, ...@@ -843,10 +886,12 @@ void GlobalHandles::AddImplicitReferences(HeapObject** parent,
void GlobalHandles::RemoveObjectGroups() { void GlobalHandles::RemoveObjectGroups() {
for (int i = 0; i < object_groups_.length(); i++) {
object_groups_.at(i)->Dispose();
}
object_groups_.Clear(); object_groups_.Clear();
for (int i = 0; i < retainer_infos_.length(); ++i) {
if (retainer_infos_[i].info != NULL)
retainer_infos_[i].info->Dispose();
}
retainer_infos_.Clear();
} }
......
...@@ -28,6 +28,7 @@ ...@@ -28,6 +28,7 @@
#ifndef V8_GLOBAL_HANDLES_H_ #ifndef V8_GLOBAL_HANDLES_H_
#define V8_GLOBAL_HANDLES_H_ #define V8_GLOBAL_HANDLES_H_
#include "../include/v8.h"
#include "../include/v8-profiler.h" #include "../include/v8-profiler.h"
#include "list.h" #include "list.h"
...@@ -44,34 +45,38 @@ namespace internal { ...@@ -44,34 +45,38 @@ namespace internal {
// An object group is treated like a single JS object: if one of object in // An object group is treated like a single JS object: if one of object in
// the group is alive, all objects in the same group are considered alive. // the group is alive, all objects in the same group are considered alive.
// An object group is used to simulate object relationship in a DOM tree. // An object group is used to simulate object relationship in a DOM tree.
class ObjectGroup {
public: struct ObjectGroupConnection {
static ObjectGroup* New(Object*** handles, ObjectGroupConnection(UniqueId id, Object** object)
size_t length, : id(id), object(object) {}
v8::RetainedObjectInfo* info) {
ASSERT(length > 0); bool operator==(const ObjectGroupConnection& other) const {
ObjectGroup* group = reinterpret_cast<ObjectGroup*>( return id == other.id;
malloc(OFFSET_OF(ObjectGroup, objects_[length])));
group->length_ = length;
group->info_ = info;
CopyWords(group->objects_, handles, static_cast<int>(length));
return group;
} }
void Dispose() { bool operator<(const ObjectGroupConnection& other) const {
if (info_ != NULL) info_->Dispose(); return id < other.id;
free(this);
} }
size_t length_; UniqueId id;
v8::RetainedObjectInfo* info_; Object** object;
Object** objects_[1]; // Variable sized array. };
private:
void* operator new(size_t size); struct ObjectGroupRetainerInfo {
void operator delete(void* p); ObjectGroupRetainerInfo(UniqueId id, RetainedObjectInfo* info)
~ObjectGroup(); : id(id), info(info) {}
DISALLOW_IMPLICIT_CONSTRUCTORS(ObjectGroup);
bool operator==(const ObjectGroupRetainerInfo& other) const {
return id == other.id;
}
bool operator<(const ObjectGroupRetainerInfo& other) const {
return id < other.id;
}
UniqueId id;
RetainedObjectInfo* info;
}; };
...@@ -213,6 +218,13 @@ class GlobalHandles { ...@@ -213,6 +218,13 @@ class GlobalHandles {
size_t length, size_t length,
v8::RetainedObjectInfo* info); v8::RetainedObjectInfo* info);
// Associates handle with the object group represented by id.
// Should be only used in GC callback function before a collection.
// All groups are destroyed after a garbage collection.
void SetObjectGroupId(Object** handle, UniqueId id);
void SetRetainedObjectInfo(UniqueId id, RetainedObjectInfo* info = NULL);
// Add an implicit references' group. // Add an implicit references' group.
// Should be only used in GC callback function before a collection. // Should be only used in GC callback function before a collection.
// All groups are destroyed after a mark-compact collection. // All groups are destroyed after a mark-compact collection.
...@@ -220,8 +232,13 @@ class GlobalHandles { ...@@ -220,8 +232,13 @@ class GlobalHandles {
Object*** children, Object*** children,
size_t length); size_t length);
// Returns the object groups. List<ObjectGroupConnection>* object_groups() {
List<ObjectGroup*>* object_groups() { return &object_groups_; } return &object_groups_;
}
List<ObjectGroupRetainerInfo>* retainer_infos() {
return &retainer_infos_;
}
// Returns the implicit references' groups. // Returns the implicit references' groups.
List<ImplicitRefGroup*>* implicit_ref_groups() { List<ImplicitRefGroup*>* implicit_ref_groups() {
...@@ -270,7 +287,8 @@ class GlobalHandles { ...@@ -270,7 +287,8 @@ class GlobalHandles {
int post_gc_processing_count_; int post_gc_processing_count_;
List<ObjectGroup*> object_groups_; List<ObjectGroupConnection> object_groups_;
List<ObjectGroupRetainerInfo> retainer_infos_;
List<ImplicitRefGroup*> implicit_ref_groups_; List<ImplicitRefGroup*> implicit_ref_groups_;
friend class Isolate; friend class Isolate;
......
...@@ -1940,17 +1940,43 @@ void NativeObjectsExplorer::FillRetainedObjects() { ...@@ -1940,17 +1940,43 @@ void NativeObjectsExplorer::FillRetainedObjects() {
const GCType major_gc_type = kGCTypeMarkSweepCompact; const GCType major_gc_type = kGCTypeMarkSweepCompact;
// Record objects that are joined into ObjectGroups. // Record objects that are joined into ObjectGroups.
isolate->heap()->CallGCPrologueCallbacks(major_gc_type); isolate->heap()->CallGCPrologueCallbacks(major_gc_type);
List<ObjectGroup*>* groups = isolate->global_handles()->object_groups();
for (int i = 0; i < groups->length(); ++i) { List<ObjectGroupConnection>* groups =
ObjectGroup* group = groups->at(i); isolate->global_handles()->object_groups();
if (group->info_ == NULL) continue; List<ObjectGroupRetainerInfo>* infos =
List<HeapObject*>* list = GetListMaybeDisposeInfo(group->info_); isolate->global_handles()->retainer_infos();
for (size_t j = 0; j < group->length_; ++j) { groups->Sort();
HeapObject* obj = HeapObject::cast(*group->objects_[j]); infos->Sort();
list->Add(obj);
in_groups_.Insert(obj); int info_ix = 0;
UniqueId current_group_id(0);
size_t current_group_start = 0;
if (groups->length() > 0) {
for (int i = 0; i <= groups->length(); ++i) {
if (i == 0)
current_group_id = groups->at(i).id;
if (i == groups->length() ||
current_group_id != groups->at(i).id) {
// Group detected: objects in indices [current_group_start, i[.
if (info_ix < infos->length() &&
infos->at(info_ix).id == groups->at(current_group_start).id) {
// Transfer the ownership of info.
List<HeapObject*>* list =
GetListMaybeDisposeInfo(infos->at(info_ix).info);
infos->at(info_ix).info = NULL;
for (int j = current_group_start; j < i; ++j) {
HeapObject* obj = HeapObject::cast(*(groups->at(j).object));
list->Add(obj);
in_groups_.Insert(obj);
}
}
if (i < groups->length()) {
current_group_id = groups->at(i).id;
current_group_start = i;
}
}
} }
group->info_ = NULL; // Acquire info object ownership.
} }
isolate->global_handles()->RemoveObjectGroups(); isolate->global_handles()->RemoveObjectGroups();
isolate->heap()->CallGCEpilogueCallbacks(major_gc_type); isolate->heap()->CallGCEpilogueCallbacks(major_gc_type);
......
This diff is collapsed.
...@@ -43,6 +43,8 @@ ...@@ -43,6 +43,8 @@
using namespace v8::internal; using namespace v8::internal;
using v8::UniqueId;
TEST(MarkingDeque) { TEST(MarkingDeque) {
CcTest::InitializeVM(); CcTest::InitializeVM();
...@@ -306,7 +308,7 @@ static void WeakPointerCallback(v8::Isolate* isolate, ...@@ -306,7 +308,7 @@ static void WeakPointerCallback(v8::Isolate* isolate,
handle.Dispose(isolate); handle.Dispose(isolate);
} }
TEST(ObjectGroups) { TEST(ObjectGroupsOldApi) {
FLAG_incremental_marking = false; FLAG_incremental_marking = false;
CcTest::InitializeVM(); CcTest::InitializeVM();
GlobalHandles* global_handles = Isolate::Current()->global_handles(); GlobalHandles* global_handles = Isolate::Current()->global_handles();
...@@ -419,6 +421,122 @@ TEST(ObjectGroups) { ...@@ -419,6 +421,122 @@ TEST(ObjectGroups) {
CHECK_EQ(7, NumberOfWeakCalls); CHECK_EQ(7, NumberOfWeakCalls);
} }
TEST(ObjectGroups) {
FLAG_incremental_marking = false;
CcTest::InitializeVM();
GlobalHandles* global_handles = reinterpret_cast<v8::internal::Isolate*>(
CcTest::isolate())->global_handles();
NumberOfWeakCalls = 0;
v8::HandleScope handle_scope(CcTest::isolate());
Heap* heap = reinterpret_cast<v8::internal::Isolate*>(
CcTest::isolate())->heap();
Handle<Object> g1s1 =
global_handles->Create(heap->AllocateFixedArray(1)->ToObjectChecked());
Handle<Object> g1s2 =
global_handles->Create(heap->AllocateFixedArray(1)->ToObjectChecked());
Handle<Object> g1c1 =
global_handles->Create(heap->AllocateFixedArray(1)->ToObjectChecked());
global_handles->MakeWeak(g1s1.location(),
reinterpret_cast<void*>(1234),
NULL,
&WeakPointerCallback);
global_handles->MakeWeak(g1s2.location(),
reinterpret_cast<void*>(1234),
NULL,
&WeakPointerCallback);
global_handles->MakeWeak(g1c1.location(),
reinterpret_cast<void*>(1234),
NULL,
&WeakPointerCallback);
Handle<Object> g2s1 =
global_handles->Create(heap->AllocateFixedArray(1)->ToObjectChecked());
Handle<Object> g2s2 =
global_handles->Create(heap->AllocateFixedArray(1)->ToObjectChecked());
Handle<Object> g2c1 =
global_handles->Create(heap->AllocateFixedArray(1)->ToObjectChecked());
global_handles->MakeWeak(g2s1.location(),
reinterpret_cast<void*>(1234),
NULL,
&WeakPointerCallback);
global_handles->MakeWeak(g2s2.location(),
reinterpret_cast<void*>(1234),
NULL,
&WeakPointerCallback);
global_handles->MakeWeak(g2c1.location(),
reinterpret_cast<void*>(1234),
NULL,
&WeakPointerCallback);
Handle<Object> root = global_handles->Create(*g1s1); // make a root.
// Connect group 1 and 2, make a cycle.
Handle<FixedArray>::cast(g1s2)->set(0, *g2s2);
Handle<FixedArray>::cast(g2s1)->set(0, *g1s1);
{
Object** g1_children[] = { g1c1.location() };
Object** g2_children[] = { g2c1.location() };
global_handles->SetObjectGroupId(g1s1.location(), v8::UniqueId(1));
global_handles->SetObjectGroupId(g1s2.location(), v8::UniqueId(1));
global_handles->AddImplicitReferences(
Handle<HeapObject>::cast(g1s1).location(), g1_children, 1);
global_handles->SetObjectGroupId(g2s1.location(), v8::UniqueId(2));
global_handles->SetObjectGroupId(g2s2.location(), v8::UniqueId(2));
global_handles->AddImplicitReferences(
Handle<HeapObject>::cast(g2s2).location(), g2_children, 1);
}
// Do a full GC
heap->CollectGarbage(OLD_POINTER_SPACE);
// All object should be alive.
CHECK_EQ(0, NumberOfWeakCalls);
// Weaken the root.
global_handles->MakeWeak(root.location(),
reinterpret_cast<void*>(1234),
NULL,
&WeakPointerCallback);
// But make children strong roots---all the objects (except for children)
// should be collectable now.
global_handles->ClearWeakness(g1c1.location());
global_handles->ClearWeakness(g2c1.location());
// Groups are deleted, rebuild groups.
{
Object** g1_children[] = { g1c1.location() };
Object** g2_children[] = { g2c1.location() };
global_handles->SetObjectGroupId(g1s1.location(), v8::UniqueId(1));
global_handles->SetObjectGroupId(g1s2.location(), v8::UniqueId(1));
global_handles->AddImplicitReferences(
Handle<HeapObject>::cast(g1s1).location(), g1_children, 1);
global_handles->SetObjectGroupId(g2s1.location(), v8::UniqueId(2));
global_handles->SetObjectGroupId(g2s2.location(), v8::UniqueId(2));
global_handles->AddImplicitReferences(
Handle<HeapObject>::cast(g2s2).location(), g2_children, 1);
}
heap->CollectGarbage(OLD_POINTER_SPACE);
// All objects should be gone. 5 global handles in total.
CHECK_EQ(5, NumberOfWeakCalls);
// And now make children weak again and collect them.
global_handles->MakeWeak(g1c1.location(),
reinterpret_cast<void*>(1234),
NULL,
&WeakPointerCallback);
global_handles->MakeWeak(g2c1.location(),
reinterpret_cast<void*>(1234),
NULL,
&WeakPointerCallback);
heap->CollectGarbage(OLD_POINTER_SPACE);
CHECK_EQ(7, NumberOfWeakCalls);
}
class TestRetainedObjectInfo : public v8::RetainedObjectInfo { class TestRetainedObjectInfo : public v8::RetainedObjectInfo {
public: public:
...@@ -444,7 +562,7 @@ class TestRetainedObjectInfo : public v8::RetainedObjectInfo { ...@@ -444,7 +562,7 @@ class TestRetainedObjectInfo : public v8::RetainedObjectInfo {
}; };
TEST(EmptyObjectGroups) { TEST(EmptyObjectGroupsOldApi) {
CcTest::InitializeVM(); CcTest::InitializeVM();
GlobalHandles* global_handles = Isolate::Current()->global_handles(); GlobalHandles* global_handles = Isolate::Current()->global_handles();
......
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