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;
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 ---
......@@ -3503,6 +3525,8 @@ class V8EXPORT V8 {
* for partially dependent handles only.
* See v8-profiler.h for RetainedObjectInfo interface description.
*/
// TODO(marja): deprecate AddObjectGroup. Use SetObjectGroupID and
// SetRetainedObjectInfo instead.
static void AddObjectGroup(Persistent<Value>* objects,
size_t length,
RetainedObjectInfo* info = NULL);
......@@ -3511,6 +3535,14 @@ class V8EXPORT V8 {
size_t length,
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
* the objects: if |parent| is alive, all |children| are alive too.
......
......@@ -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,
Persistent<Value>* children,
size_t length) {
......
......@@ -36,11 +36,6 @@ namespace v8 {
namespace internal {
ObjectGroup::~ObjectGroup() {
if (info_ != NULL) info_->Dispose();
}
class GlobalHandles::Node {
public:
// State transition diagram:
......@@ -578,45 +573,77 @@ void GlobalHandles::IterateNewSpaceWeakIndependentRoots(ObjectVisitor* v) {
bool GlobalHandles::IterateObjectGroups(ObjectVisitor* v,
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;
for (int i = 0; i < object_groups_.length(); i++) {
ObjectGroup* entry = object_groups_.at(i);
ASSERT(entry != NULL);
Object*** objects = entry->objects_;
bool group_should_be_visited = false;
for (size_t j = 0; j < entry->length_; j++) {
Object* object = *objects[j];
if (object->IsHeapObject()) {
if (!can_skip(isolate_->heap(), &object)) {
group_should_be_visited = true;
break;
for (int i = 0; i <= object_groups_.length(); ++i) {
if (i == 0)
current_group_id = object_groups_[i].id;
if (i == object_groups_.length() ||
current_group_id != object_groups_[i].id) {
// Group detected: objects in indices [current_group_start, i[.
bool group_should_be_visited = false;
for (int j = current_group_start; j < i; ++j) {
Object* object = *(object_groups_[j].object);
if (object->IsHeapObject()) {
if (!can_skip(isolate_->heap(), &object)) {
group_should_be_visited = true;
break;
}
}
}
}
if (!group_should_be_visited) {
object_groups_[last++] = entry;
continue;
}
if (!group_should_be_visited) {
for (int j = current_group_start; j < i; ++j)
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
// objects in the group.
for (size_t j = 0; j < entry->length_; ++j) {
Object* object = *objects[j];
if (object->IsHeapObject()) {
v->VisitPointer(&object);
any_group_was_visited = true;
if (info_index < retainer_infos_.length() &&
retainer_infos_[info_index].id ==
object_groups_[current_group_start].id) {
// This object group has an associated ObjectGroupRetainerInfo.
if (!group_should_be_visited) {
retainer_infos_[surviving_info_index++] =
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;
}
......@@ -824,7 +851,23 @@ void GlobalHandles::AddObjectGroup(Object*** handles,
if (info != NULL) info->Dispose();
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,
void GlobalHandles::RemoveObjectGroups() {
for (int i = 0; i < object_groups_.length(); i++) {
object_groups_.at(i)->Dispose();
}
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 @@
#ifndef V8_GLOBAL_HANDLES_H_
#define V8_GLOBAL_HANDLES_H_
#include "../include/v8.h"
#include "../include/v8-profiler.h"
#include "list.h"
......@@ -44,34 +45,38 @@ namespace internal {
// 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.
// An object group is used to simulate object relationship in a DOM tree.
class ObjectGroup {
public:
static ObjectGroup* New(Object*** handles,
size_t length,
v8::RetainedObjectInfo* info) {
ASSERT(length > 0);
ObjectGroup* group = reinterpret_cast<ObjectGroup*>(
malloc(OFFSET_OF(ObjectGroup, objects_[length])));
group->length_ = length;
group->info_ = info;
CopyWords(group->objects_, handles, static_cast<int>(length));
return group;
struct ObjectGroupConnection {
ObjectGroupConnection(UniqueId id, Object** object)
: id(id), object(object) {}
bool operator==(const ObjectGroupConnection& other) const {
return id == other.id;
}
void Dispose() {
if (info_ != NULL) info_->Dispose();
free(this);
bool operator<(const ObjectGroupConnection& other) const {
return id < other.id;
}
size_t length_;
v8::RetainedObjectInfo* info_;
Object** objects_[1]; // Variable sized array.
UniqueId id;
Object** object;
};
private:
void* operator new(size_t size);
void operator delete(void* p);
~ObjectGroup();
DISALLOW_IMPLICIT_CONSTRUCTORS(ObjectGroup);
struct ObjectGroupRetainerInfo {
ObjectGroupRetainerInfo(UniqueId id, RetainedObjectInfo* info)
: id(id), info(info) {}
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 {
size_t length,
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.
// Should be only used in GC callback function before a collection.
// All groups are destroyed after a mark-compact collection.
......@@ -220,8 +232,13 @@ class GlobalHandles {
Object*** children,
size_t length);
// Returns the object groups.
List<ObjectGroup*>* object_groups() { return &object_groups_; }
List<ObjectGroupConnection>* object_groups() {
return &object_groups_;
}
List<ObjectGroupRetainerInfo>* retainer_infos() {
return &retainer_infos_;
}
// Returns the implicit references' groups.
List<ImplicitRefGroup*>* implicit_ref_groups() {
......@@ -270,7 +287,8 @@ class GlobalHandles {
int post_gc_processing_count_;
List<ObjectGroup*> object_groups_;
List<ObjectGroupConnection> object_groups_;
List<ObjectGroupRetainerInfo> retainer_infos_;
List<ImplicitRefGroup*> implicit_ref_groups_;
friend class Isolate;
......
......@@ -1940,17 +1940,43 @@ void NativeObjectsExplorer::FillRetainedObjects() {
const GCType major_gc_type = kGCTypeMarkSweepCompact;
// Record objects that are joined into ObjectGroups.
isolate->heap()->CallGCPrologueCallbacks(major_gc_type);
List<ObjectGroup*>* groups = isolate->global_handles()->object_groups();
for (int i = 0; i < groups->length(); ++i) {
ObjectGroup* group = groups->at(i);
if (group->info_ == NULL) continue;
List<HeapObject*>* list = GetListMaybeDisposeInfo(group->info_);
for (size_t j = 0; j < group->length_; ++j) {
HeapObject* obj = HeapObject::cast(*group->objects_[j]);
list->Add(obj);
in_groups_.Insert(obj);
List<ObjectGroupConnection>* groups =
isolate->global_handles()->object_groups();
List<ObjectGroupRetainerInfo>* infos =
isolate->global_handles()->retainer_infos();
groups->Sort();
infos->Sort();
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->heap()->CallGCEpilogueCallbacks(major_gc_type);
......
This diff is collapsed.
......@@ -43,6 +43,8 @@
using namespace v8::internal;
using v8::UniqueId;
TEST(MarkingDeque) {
CcTest::InitializeVM();
......@@ -306,7 +308,7 @@ static void WeakPointerCallback(v8::Isolate* isolate,
handle.Dispose(isolate);
}
TEST(ObjectGroups) {
TEST(ObjectGroupsOldApi) {
FLAG_incremental_marking = false;
CcTest::InitializeVM();
GlobalHandles* global_handles = Isolate::Current()->global_handles();
......@@ -419,6 +421,122 @@ TEST(ObjectGroups) {
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 {
public:
......@@ -444,7 +562,7 @@ class TestRetainedObjectInfo : public v8::RetainedObjectInfo {
};
TEST(EmptyObjectGroups) {
TEST(EmptyObjectGroupsOldApi) {
CcTest::InitializeVM();
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