Commit 41eb2f22 authored by vitalyr@chromium.org's avatar vitalyr@chromium.org

External string table.

Instead of weak handles external strings use a separate table.  This
table uses 5 times less memory than weak handles.  Moreover, since we
don't have to follow the weak handle callback protocol we can collect
the strings faster and even on scavenge collections.

Review URL: http://codereview.chromium.org/467037

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@3439 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
parent 67f6ea67
...@@ -833,13 +833,26 @@ class V8EXPORT String : public Primitive { ...@@ -833,13 +833,26 @@ class V8EXPORT String : public Primitive {
* Returns true if the string is both external and ascii * Returns true if the string is both external and ascii
*/ */
bool IsExternalAscii() const; bool IsExternalAscii() const;
class V8EXPORT ExternalStringResourceBase {
public:
virtual ~ExternalStringResourceBase() {}
protected:
ExternalStringResourceBase() {}
private:
// Disallow copying and assigning.
ExternalStringResourceBase(const ExternalStringResourceBase&);
void operator=(const ExternalStringResourceBase&);
};
/** /**
* An ExternalStringResource is a wrapper around a two-byte string * An ExternalStringResource is a wrapper around a two-byte string
* buffer that resides outside V8's heap. Implement an * buffer that resides outside V8's heap. Implement an
* ExternalStringResource to manage the life cycle of the underlying * ExternalStringResource to manage the life cycle of the underlying
* buffer. Note that the string data must be immutable. * buffer. Note that the string data must be immutable.
*/ */
class V8EXPORT ExternalStringResource { // NOLINT class V8EXPORT ExternalStringResource
: public ExternalStringResourceBase {
public: public:
/** /**
* Override the destructor to manage the life cycle of the underlying * Override the destructor to manage the life cycle of the underlying
...@@ -852,10 +865,6 @@ class V8EXPORT String : public Primitive { ...@@ -852,10 +865,6 @@ class V8EXPORT String : public Primitive {
virtual size_t length() const = 0; virtual size_t length() const = 0;
protected: protected:
ExternalStringResource() {} ExternalStringResource() {}
private:
// Disallow copying and assigning.
ExternalStringResource(const ExternalStringResource&);
void operator=(const ExternalStringResource&);
}; };
/** /**
...@@ -869,7 +878,8 @@ class V8EXPORT String : public Primitive { ...@@ -869,7 +878,8 @@ class V8EXPORT String : public Primitive {
* Use String::New or convert to 16 bit data for non-ASCII. * Use String::New or convert to 16 bit data for non-ASCII.
*/ */
class V8EXPORT ExternalAsciiStringResource { // NOLINT class V8EXPORT ExternalAsciiStringResource
: public ExternalStringResourceBase {
public: public:
/** /**
* Override the destructor to manage the life cycle of the underlying * Override the destructor to manage the life cycle of the underlying
...@@ -882,10 +892,6 @@ class V8EXPORT String : public Primitive { ...@@ -882,10 +892,6 @@ class V8EXPORT String : public Primitive {
virtual size_t length() const = 0; virtual size_t length() const = 0;
protected: protected:
ExternalAsciiStringResource() {} ExternalAsciiStringResource() {}
private:
// Disallow copying and assigning.
ExternalAsciiStringResource(const ExternalAsciiStringResource&);
void operator=(const ExternalAsciiStringResource&);
}; };
/** /**
......
...@@ -3082,81 +3082,13 @@ i::Handle<i::String> NewExternalAsciiStringHandle( ...@@ -3082,81 +3082,13 @@ i::Handle<i::String> NewExternalAsciiStringHandle(
} }
static void DisposeExternalString(v8::Persistent<v8::Value> obj,
void* parameter) {
ENTER_V8;
i::ExternalTwoByteString* str =
i::ExternalTwoByteString::cast(*Utils::OpenHandle(*obj));
// External symbols are deleted when they are pruned out of the symbol
// table. Generally external symbols are not registered with the weak handle
// callbacks unless they are upgraded to a symbol after being externalized.
if (!str->IsSymbol()) {
v8::String::ExternalStringResource* resource =
reinterpret_cast<v8::String::ExternalStringResource*>(parameter);
if (resource != NULL) {
const int total_size =
static_cast<int>(resource->length() * sizeof(*resource->data()));
i::Counters::total_external_string_memory.Decrement(total_size);
// The object will continue to live in the JavaScript heap until the
// handle is entirely cleaned out by the next GC. For example the
// destructor for the resource below could bring it back to life again.
// Which is why we make sure to not have a dangling pointer here.
str->set_resource(NULL);
delete resource;
}
}
// In any case we do not need this handle any longer.
obj.Dispose();
}
static void DisposeExternalAsciiString(v8::Persistent<v8::Value> obj,
void* parameter) {
ENTER_V8;
i::ExternalAsciiString* str =
i::ExternalAsciiString::cast(*Utils::OpenHandle(*obj));
// External symbols are deleted when they are pruned out of the symbol
// table. Generally external symbols are not registered with the weak handle
// callbacks unless they are upgraded to a symbol after being externalized.
if (!str->IsSymbol()) {
v8::String::ExternalAsciiStringResource* resource =
reinterpret_cast<v8::String::ExternalAsciiStringResource*>(parameter);
if (resource != NULL) {
const int total_size =
static_cast<int>(resource->length() * sizeof(*resource->data()));
i::Counters::total_external_string_memory.Decrement(total_size);
// The object will continue to live in the JavaScript heap until the
// handle is entirely cleaned out by the next GC. For example the
// destructor for the resource below could bring it back to life again.
// Which is why we make sure to not have a dangling pointer here.
str->set_resource(NULL);
delete resource;
}
}
// In any case we do not need this handle any longer.
obj.Dispose();
}
Local<String> v8::String::NewExternal( Local<String> v8::String::NewExternal(
v8::String::ExternalStringResource* resource) { v8::String::ExternalStringResource* resource) {
EnsureInitialized("v8::String::NewExternal()"); EnsureInitialized("v8::String::NewExternal()");
LOG_API("String::NewExternal"); LOG_API("String::NewExternal");
ENTER_V8; ENTER_V8;
const int total_size =
static_cast<int>(resource->length() * sizeof(*resource->data()));
i::Counters::total_external_string_memory.Increment(total_size);
i::Handle<i::String> result = NewExternalStringHandle(resource); i::Handle<i::String> result = NewExternalStringHandle(resource);
i::Handle<i::Object> handle = i::GlobalHandles::Create(*result); i::ExternalStringTable::AddString(*result);
i::GlobalHandles::MakeWeak(handle.location(),
resource,
&DisposeExternalString);
return Utils::ToLocal(result); return Utils::ToLocal(result);
} }
...@@ -3168,13 +3100,7 @@ bool v8::String::MakeExternal(v8::String::ExternalStringResource* resource) { ...@@ -3168,13 +3100,7 @@ bool v8::String::MakeExternal(v8::String::ExternalStringResource* resource) {
i::Handle<i::String> obj = Utils::OpenHandle(this); i::Handle<i::String> obj = Utils::OpenHandle(this);
bool result = obj->MakeExternal(resource); bool result = obj->MakeExternal(resource);
if (result && !obj->IsSymbol()) { if (result && !obj->IsSymbol()) {
// Operation was successful and the string is not a symbol. In this case i::ExternalStringTable::AddString(*obj);
// we need to make sure that the we call the destructor for the external
// resource when no strong references to the string remain.
i::Handle<i::Object> handle = i::GlobalHandles::Create(*obj);
i::GlobalHandles::MakeWeak(handle.location(),
resource,
&DisposeExternalString);
} }
return result; return result;
} }
...@@ -3185,14 +3111,8 @@ Local<String> v8::String::NewExternal( ...@@ -3185,14 +3111,8 @@ Local<String> v8::String::NewExternal(
EnsureInitialized("v8::String::NewExternal()"); EnsureInitialized("v8::String::NewExternal()");
LOG_API("String::NewExternal"); LOG_API("String::NewExternal");
ENTER_V8; ENTER_V8;
const int total_size =
static_cast<int>(resource->length() * sizeof(*resource->data()));
i::Counters::total_external_string_memory.Increment(total_size);
i::Handle<i::String> result = NewExternalAsciiStringHandle(resource); i::Handle<i::String> result = NewExternalAsciiStringHandle(resource);
i::Handle<i::Object> handle = i::GlobalHandles::Create(*result); i::ExternalStringTable::AddString(*result);
i::GlobalHandles::MakeWeak(handle.location(),
resource,
&DisposeExternalAsciiString);
return Utils::ToLocal(result); return Utils::ToLocal(result);
} }
...@@ -3205,13 +3125,7 @@ bool v8::String::MakeExternal( ...@@ -3205,13 +3125,7 @@ bool v8::String::MakeExternal(
i::Handle<i::String> obj = Utils::OpenHandle(this); i::Handle<i::String> obj = Utils::OpenHandle(this);
bool result = obj->MakeExternal(resource); bool result = obj->MakeExternal(resource);
if (result && !obj->IsSymbol()) { if (result && !obj->IsSymbol()) {
// Operation was successful and the string is not a symbol. In this case i::ExternalStringTable::AddString(*obj);
// we need to make sure that the we call the destructor for the external
// resource when no strong references to the string remain.
i::Handle<i::Object> handle = i::GlobalHandles::Create(*obj);
i::GlobalHandles::MakeWeak(handle.location(),
resource,
&DisposeExternalAsciiString);
} }
return result; return result;
} }
......
...@@ -168,6 +168,12 @@ class GlobalHandles::Node : public Malloced { ...@@ -168,6 +168,12 @@ class GlobalHandles::Node : public Malloced {
if (first_deallocated()) { if (first_deallocated()) {
first_deallocated()->set_next(head()); first_deallocated()->set_next(head());
} }
// Check that we are not passing a finalized external string to
// the callback.
ASSERT(!object_->IsExternalAsciiString() ||
ExternalAsciiString::cast(object_)->resource() != NULL);
ASSERT(!object_->IsExternalTwoByteString() ||
ExternalTwoByteString::cast(object_)->resource() != NULL);
// Leaving V8. // Leaving V8.
VMState state(EXTERNAL); VMState state(EXTERNAL);
func(object, par); func(object, par);
...@@ -507,5 +513,4 @@ void GlobalHandles::RemoveObjectGroups() { ...@@ -507,5 +513,4 @@ void GlobalHandles::RemoveObjectGroups() {
object_groups->Clear(); object_groups->Clear();
} }
} } // namespace v8::internal } } // namespace v8::internal
...@@ -294,7 +294,7 @@ enum GarbageCollector { SCAVENGER, MARK_COMPACTOR }; ...@@ -294,7 +294,7 @@ enum GarbageCollector { SCAVENGER, MARK_COMPACTOR };
enum Executability { NOT_EXECUTABLE, EXECUTABLE }; enum Executability { NOT_EXECUTABLE, EXECUTABLE };
enum VisitMode { VISIT_ALL, VISIT_ONLY_STRONG }; enum VisitMode { VISIT_ALL, VISIT_ALL_IN_SCAVENGE, VISIT_ONLY_STRONG };
// A CodeDesc describes a buffer holding instructions and relocation // A CodeDesc describes a buffer holding instructions and relocation
......
...@@ -109,6 +109,19 @@ Object* Heap::NumberFromUint32(uint32_t value) { ...@@ -109,6 +109,19 @@ Object* Heap::NumberFromUint32(uint32_t value) {
} }
void Heap::FinalizeExternalString(String* string) {
ASSERT(string->IsExternalString());
v8::String::ExternalStringResourceBase** resource_addr =
reinterpret_cast<v8::String::ExternalStringResourceBase**>(
reinterpret_cast<byte*>(string) +
ExternalString::kResourceOffset -
kHeapObjectTag);
delete *resource_addr;
// Clear the resource pointer in the string.
*resource_addr = NULL;
}
Object* Heap::AllocateRawMap() { Object* Heap::AllocateRawMap() {
#ifdef DEBUG #ifdef DEBUG
Counters::objs_since_last_full.Increment(); Counters::objs_since_last_full.Increment();
...@@ -321,6 +334,56 @@ inline bool Heap::allow_allocation(bool new_state) { ...@@ -321,6 +334,56 @@ inline bool Heap::allow_allocation(bool new_state) {
#endif #endif
void ExternalStringTable::AddString(String* string) {
ASSERT(string->IsExternalString());
if (Heap::InNewSpace(string)) {
new_space_strings_.Add(string);
} else {
old_space_strings_.Add(string);
}
}
void ExternalStringTable::Iterate(ObjectVisitor* v) {
if (!new_space_strings_.is_empty()) {
Object** start = &new_space_strings_[0];
v->VisitPointers(start, start + new_space_strings_.length());
}
if (!old_space_strings_.is_empty()) {
Object** start = &old_space_strings_[0];
v->VisitPointers(start, start + old_space_strings_.length());
}
}
// Verify() is inline to avoid ifdef-s around its calls in release
// mode.
void ExternalStringTable::Verify() {
#ifdef DEBUG
for (int i = 0; i < new_space_strings_.length(); ++i) {
ASSERT(Heap::InNewSpace(new_space_strings_[i]));
ASSERT(new_space_strings_[i] != Heap::raw_unchecked_null_value());
}
for (int i = 0; i < old_space_strings_.length(); ++i) {
ASSERT(!Heap::InNewSpace(old_space_strings_[i]));
ASSERT(old_space_strings_[i] != Heap::raw_unchecked_null_value());
}
#endif
}
void ExternalStringTable::AddOldString(String* string) {
ASSERT(string->IsExternalString());
ASSERT(!Heap::InNewSpace(string));
old_space_strings_.Add(string);
}
void ExternalStringTable::ShrinkNewStrings(int position) {
new_space_strings_.Rewind(position);
Verify();
}
} } // namespace v8::internal } } // namespace v8::internal
#endif // V8_HEAP_INL_H_ #endif // V8_HEAP_INL_H_
...@@ -733,7 +733,7 @@ void Heap::Scavenge() { ...@@ -733,7 +733,7 @@ void Heap::Scavenge() {
ScavengeVisitor scavenge_visitor; ScavengeVisitor scavenge_visitor;
// Copy roots. // Copy roots.
IterateRoots(&scavenge_visitor, VISIT_ALL); IterateRoots(&scavenge_visitor, VISIT_ALL_IN_SCAVENGE);
// Copy objects reachable from the old generation. By definition, // Copy objects reachable from the old generation. By definition,
// there are no intergenerational pointers in code or data spaces. // there are no intergenerational pointers in code or data spaces.
...@@ -753,6 +753,63 @@ void Heap::Scavenge() { ...@@ -753,6 +753,63 @@ void Heap::Scavenge() {
} }
} }
new_space_front = DoScavenge(&scavenge_visitor, new_space_front);
ScavengeExternalStringTable();
ASSERT(new_space_front == new_space_.top());
// Set age mark.
new_space_.set_age_mark(new_space_.top());
// Update how much has survived scavenge.
survived_since_last_expansion_ +=
(PromotedSpaceSize() - survived_watermark) + new_space_.Size();
LOG(ResourceEvent("scavenge", "end"));
gc_state_ = NOT_IN_GC;
}
void Heap::ScavengeExternalStringTable() {
ExternalStringTable::Verify();
if (ExternalStringTable::new_space_strings_.is_empty()) return;
Object** start = &ExternalStringTable::new_space_strings_[0];
Object** end = start + ExternalStringTable::new_space_strings_.length();
Object** last = start;
for (Object** p = start; p < end; ++p) {
ASSERT(Heap::InFromSpace(*p));
MapWord first_word = HeapObject::cast(*p)->map_word();
if (!first_word.IsForwardingAddress()) {
// Unreachable external string can be finalized.
FinalizeExternalString(String::cast(*p));
continue;
}
// String is still reachable.
String* target = String::cast(first_word.ToForwardingAddress());
ASSERT(target->IsExternalString());
if (Heap::InNewSpace(target)) {
// String is still in new space. Update the table entry.
*last = target;
++last;
} else {
// String got promoted. Move it to the old string list.
ExternalStringTable::AddOldString(target);
}
}
ExternalStringTable::ShrinkNewStrings(last - start);
}
Address Heap::DoScavenge(ObjectVisitor* scavenge_visitor,
Address new_space_front) {
do { do {
ASSERT(new_space_front <= new_space_.top()); ASSERT(new_space_front <= new_space_.top());
...@@ -761,7 +818,7 @@ void Heap::Scavenge() { ...@@ -761,7 +818,7 @@ void Heap::Scavenge() {
// queue is empty. // queue is empty.
while (new_space_front < new_space_.top()) { while (new_space_front < new_space_.top()) {
HeapObject* object = HeapObject::FromAddress(new_space_front); HeapObject* object = HeapObject::FromAddress(new_space_front);
object->Iterate(&scavenge_visitor); object->Iterate(scavenge_visitor);
new_space_front += object->Size(); new_space_front += object->Size();
} }
...@@ -783,7 +840,7 @@ void Heap::Scavenge() { ...@@ -783,7 +840,7 @@ void Heap::Scavenge() {
RecordCopiedObject(target); RecordCopiedObject(target);
#endif #endif
// Visit the newly copied object for pointers to new space. // Visit the newly copied object for pointers to new space.
target->Iterate(&scavenge_visitor); target->Iterate(scavenge_visitor);
UpdateRSet(target); UpdateRSet(target);
} }
...@@ -791,16 +848,7 @@ void Heap::Scavenge() { ...@@ -791,16 +848,7 @@ void Heap::Scavenge() {
// (there are currently no more unswept promoted objects). // (there are currently no more unswept promoted objects).
} while (new_space_front < new_space_.top()); } while (new_space_front < new_space_.top());
// Set age mark. return new_space_front;
new_space_.set_age_mark(new_space_.top());
// Update how much has survived scavenge.
survived_since_last_expansion_ +=
(PromotedSpaceSize() - survived_watermark) + new_space_.Size();
LOG(ResourceEvent("scavenge", "end"));
gc_state_ = NOT_IN_GC;
} }
...@@ -3175,6 +3223,11 @@ void Heap::IterateRoots(ObjectVisitor* v, VisitMode mode) { ...@@ -3175,6 +3223,11 @@ void Heap::IterateRoots(ObjectVisitor* v, VisitMode mode) {
IterateStrongRoots(v, mode); IterateStrongRoots(v, mode);
v->VisitPointer(reinterpret_cast<Object**>(&roots_[kSymbolTableRootIndex])); v->VisitPointer(reinterpret_cast<Object**>(&roots_[kSymbolTableRootIndex]));
v->Synchronize("symbol_table"); v->Synchronize("symbol_table");
if (mode != VISIT_ALL_IN_SCAVENGE) {
// Scavenge collections have special processing for this.
ExternalStringTable::Iterate(v);
}
v->Synchronize("external_string_table");
} }
...@@ -3203,11 +3256,12 @@ void Heap::IterateStrongRoots(ObjectVisitor* v, VisitMode mode) { ...@@ -3203,11 +3256,12 @@ void Heap::IterateStrongRoots(ObjectVisitor* v, VisitMode mode) {
HandleScopeImplementer::Iterate(v); HandleScopeImplementer::Iterate(v);
v->Synchronize("handlescope"); v->Synchronize("handlescope");
// Iterate over the builtin code objects and code stubs in the heap. Note // Iterate over the builtin code objects and code stubs in the
// that it is not strictly necessary to iterate over code objects on // heap. Note that it is not necessary to iterate over code objects
// scavenge collections. We still do it here because this same function // on scavenge collections.
// is used by the mark-sweep collector and the deserializer. if (mode != VISIT_ALL_IN_SCAVENGE) {
Builtins::IterateBuiltins(v); Builtins::IterateBuiltins(v);
}
v->Synchronize("builtins"); v->Synchronize("builtins");
// Iterate over global handles. // Iterate over global handles.
...@@ -3424,6 +3478,8 @@ void Heap::SetStackLimits() { ...@@ -3424,6 +3478,8 @@ void Heap::SetStackLimits() {
void Heap::TearDown() { void Heap::TearDown() {
GlobalHandles::TearDown(); GlobalHandles::TearDown();
ExternalStringTable::TearDown();
new_space_.TearDown(); new_space_.TearDown();
if (old_pointer_space_ != NULL) { if (old_pointer_space_ != NULL) {
...@@ -3839,8 +3895,8 @@ class MarkRootVisitor: public ObjectVisitor { ...@@ -3839,8 +3895,8 @@ class MarkRootVisitor: public ObjectVisitor {
// Triggers a depth-first traversal of reachable objects from roots // Triggers a depth-first traversal of reachable objects from roots
// and finds a path to a specific heap object and prints it. // and finds a path to a specific heap object and prints it.
void Heap::TracePathToObject() { void Heap::TracePathToObject(Object* target) {
search_target = NULL; search_target = target;
search_for_any_global = false; search_for_any_global = false;
MarkRootVisitor root_visitor; MarkRootVisitor root_visitor;
...@@ -3991,4 +4047,35 @@ void TranscendentalCache::Clear() { ...@@ -3991,4 +4047,35 @@ void TranscendentalCache::Clear() {
} }
void ExternalStringTable::CleanUp() {
int last = 0;
for (int i = 0; i < new_space_strings_.length(); ++i) {
if (new_space_strings_[i] == Heap::raw_unchecked_null_value()) continue;
if (Heap::InNewSpace(new_space_strings_[i])) {
new_space_strings_[last++] = new_space_strings_[i];
} else {
old_space_strings_.Add(new_space_strings_[i]);
}
}
new_space_strings_.Rewind(last);
last = 0;
for (int i = 0; i < old_space_strings_.length(); ++i) {
if (old_space_strings_[i] == Heap::raw_unchecked_null_value()) continue;
ASSERT(!Heap::InNewSpace(old_space_strings_[i]));
old_space_strings_[last++] = old_space_strings_[i];
}
old_space_strings_.Rewind(last);
Verify();
}
void ExternalStringTable::TearDown() {
new_space_strings_.Free();
old_space_strings_.Free();
}
List<Object*> ExternalStringTable::new_space_strings_;
List<Object*> ExternalStringTable::old_space_strings_;
} } // namespace v8::internal } } // namespace v8::internal
...@@ -566,6 +566,10 @@ class Heap : public AllStatic { ...@@ -566,6 +566,10 @@ class Heap : public AllStatic {
static Object* AllocateExternalStringFromTwoByte( static Object* AllocateExternalStringFromTwoByte(
ExternalTwoByteString::Resource* resource); ExternalTwoByteString::Resource* resource);
// Finalizes an external string by deleting the associated external
// data and clearing the resource pointer.
static inline void FinalizeExternalString(String* string);
// Allocates an uninitialized object. The memory is non-executable if the // Allocates an uninitialized object. The memory is non-executable if the
// hardware and OS allow. // hardware and OS allow.
// Returns Failure::RetryAfterGC(requested_bytes, space) if the allocation // Returns Failure::RetryAfterGC(requested_bytes, space) if the allocation
...@@ -778,7 +782,7 @@ class Heap : public AllStatic { ...@@ -778,7 +782,7 @@ class Heap : public AllStatic {
return disallow_allocation_failure_; return disallow_allocation_failure_;
} }
static void TracePathToObject(); static void TracePathToObject(Object* target);
static void TracePathToGlobal(); static void TracePathToGlobal();
#endif #endif
...@@ -1039,6 +1043,9 @@ class Heap : public AllStatic { ...@@ -1039,6 +1043,9 @@ class Heap : public AllStatic {
// Performs a minor collection in new generation. // Performs a minor collection in new generation.
static void Scavenge(); static void Scavenge();
static void ScavengeExternalStringTable();
static Address DoScavenge(ObjectVisitor* scavenge_visitor,
Address new_space_front);
// Performs a major collection in the whole heap. // Performs a major collection in the whole heap.
static void MarkCompact(GCTracer* tracer); static void MarkCompact(GCTracer* tracer);
...@@ -1623,6 +1630,39 @@ class TranscendentalCache { ...@@ -1623,6 +1630,39 @@ class TranscendentalCache {
}; };
// External strings table is a place where all external strings are
// registered. We need to keep track of such strings to properly
// finalize them.
class ExternalStringTable : public AllStatic {
public:
// Registers an external string.
inline static void AddString(String* string);
inline static void Iterate(ObjectVisitor* v);
// Restores internal invariant and gets rid of collected strings.
// Must be called after each Iterate() that modified the strings.
static void CleanUp();
// Destroys all allocated memory.
static void TearDown();
private:
friend class Heap;
inline static void Verify();
inline static void AddOldString(String* string);
// Notifies the table that only a prefix of the new list is valid.
inline static void ShrinkNewStrings(int position);
// To speed up scavenge collections new space string are kept
// separate from old space strings.
static List<Object*> new_space_strings_;
static List<Object*> old_space_strings_;
};
} } // namespace v8::internal } } // namespace v8::internal
#endif // V8_HEAP_H_ #endif // V8_HEAP_H_
...@@ -155,6 +155,8 @@ void MarkCompactCollector::Finish() { ...@@ -155,6 +155,8 @@ void MarkCompactCollector::Finish() {
// objects (empty string, illegal builtin). // objects (empty string, illegal builtin).
StubCache::Clear(); StubCache::Clear();
ExternalStringTable::CleanUp();
// If we've just compacted old space there's no reason to check the // If we've just compacted old space there's no reason to check the
// fragmentation limit. Just return. // fragmentation limit. Just return.
if (HasCompacted()) return; if (HasCompacted()) return;
...@@ -369,41 +371,18 @@ class RootMarkingVisitor : public ObjectVisitor { ...@@ -369,41 +371,18 @@ class RootMarkingVisitor : public ObjectVisitor {
class SymbolTableCleaner : public ObjectVisitor { class SymbolTableCleaner : public ObjectVisitor {
public: public:
SymbolTableCleaner() : pointers_removed_(0) { } SymbolTableCleaner() : pointers_removed_(0) { }
void VisitPointers(Object** start, Object** end) {
virtual void VisitPointers(Object** start, Object** end) {
// Visit all HeapObject pointers in [start, end). // Visit all HeapObject pointers in [start, end).
for (Object** p = start; p < end; p++) { for (Object** p = start; p < end; p++) {
if ((*p)->IsHeapObject() && !HeapObject::cast(*p)->IsMarked()) { if ((*p)->IsHeapObject() && !HeapObject::cast(*p)->IsMarked()) {
// Check if the symbol being pruned is an external symbol. We need to // Check if the symbol being pruned is an external symbol. We need to
// delete the associated external data as this symbol is going away. // delete the associated external data as this symbol is going away.
// Since the object is not marked we can access its map word safely
// without having to worry about marking bits in the object header.
Map* map = HeapObject::cast(*p)->map();
// Since no objects have yet been moved we can safely access the map of // Since no objects have yet been moved we can safely access the map of
// the object. // the object.
uint32_t type = map->instance_type(); if ((*p)->IsExternalString()) {
bool is_external = (type & kStringRepresentationMask) == Heap::FinalizeExternalString(String::cast(*p));
kExternalStringTag;
if (is_external) {
bool is_two_byte = (type & kStringEncodingMask) == kTwoByteStringTag;
byte* resource_addr = reinterpret_cast<byte*>(*p) +
ExternalString::kResourceOffset -
kHeapObjectTag;
if (is_two_byte) {
v8::String::ExternalStringResource** resource =
reinterpret_cast<v8::String::ExternalStringResource**>
(resource_addr);
delete *resource;
// Clear the resource pointer in the symbol.
*resource = NULL;
} else {
v8::String::ExternalAsciiStringResource** resource =
reinterpret_cast<v8::String::ExternalAsciiStringResource**>
(resource_addr);
delete *resource;
// Clear the resource pointer in the symbol.
*resource = NULL;
}
} }
// Set the entry to null_value (as deleted). // Set the entry to null_value (as deleted).
*p = Heap::raw_unchecked_null_value(); *p = Heap::raw_unchecked_null_value();
...@@ -546,34 +525,7 @@ bool MarkCompactCollector::IsUnmarkedHeapObject(Object** p) { ...@@ -546,34 +525,7 @@ bool MarkCompactCollector::IsUnmarkedHeapObject(Object** p) {
} }
class SymbolMarkingVisitor : public ObjectVisitor {
public:
void VisitPointers(Object** start, Object** end) {
MarkingVisitor marker;
for (Object** p = start; p < end; p++) {
if (!(*p)->IsHeapObject()) continue;
HeapObject* object = HeapObject::cast(*p);
// If the object is marked, we have marked or are in the process
// of marking subparts.
if (object->IsMarked()) continue;
// The object is unmarked, we do not need to unmark to use its
// map.
Map* map = object->map();
object->IterateBody(map->instance_type(),
object->SizeFromMap(map),
&marker);
}
}
};
void MarkCompactCollector::MarkSymbolTable() { void MarkCompactCollector::MarkSymbolTable() {
// Objects reachable from symbols are marked as live so as to ensure
// that if the symbol itself remains alive after GC for any reason,
// and if it is a cons string backed by an external string (even indirectly),
// then the external string does not receive a weak reference callback.
SymbolTable* symbol_table = Heap::raw_unchecked_symbol_table(); SymbolTable* symbol_table = Heap::raw_unchecked_symbol_table();
// Mark the symbol table itself. // Mark the symbol table itself.
SetMark(symbol_table); SetMark(symbol_table);
...@@ -581,11 +533,6 @@ void MarkCompactCollector::MarkSymbolTable() { ...@@ -581,11 +533,6 @@ void MarkCompactCollector::MarkSymbolTable() {
MarkingVisitor marker; MarkingVisitor marker;
symbol_table->IteratePrefix(&marker); symbol_table->IteratePrefix(&marker);
ProcessMarkingStack(&marker); ProcessMarkingStack(&marker);
// Mark subparts of the symbols but not the symbols themselves
// (unless reachable from another symbol).
SymbolMarkingVisitor symbol_marker;
symbol_table->IterateElements(&symbol_marker);
ProcessMarkingStack(&marker);
} }
...@@ -774,6 +721,8 @@ void MarkCompactCollector::MarkLiveObjects() { ...@@ -774,6 +721,8 @@ void MarkCompactCollector::MarkLiveObjects() {
SymbolTableCleaner v; SymbolTableCleaner v;
symbol_table->IterateElements(&v); symbol_table->IterateElements(&v);
symbol_table->ElementsRemoved(v.PointersRemoved()); symbol_table->ElementsRemoved(v.PointersRemoved());
ExternalStringTable::Iterate(&v);
ExternalStringTable::CleanUp();
// Remove object groups after marking phase. // Remove object groups after marking phase.
GlobalHandles::RemoveObjectGroups(); GlobalHandles::RemoveObjectGroups();
......
...@@ -74,8 +74,6 @@ namespace internal { ...@@ -74,8 +74,6 @@ namespace internal {
SC(objs_since_last_full, V8.ObjsSinceLastFull) \ SC(objs_since_last_full, V8.ObjsSinceLastFull) \
SC(symbol_table_capacity, V8.SymbolTableCapacity) \ SC(symbol_table_capacity, V8.SymbolTableCapacity) \
SC(number_of_symbols, V8.NumberOfSymbols) \ SC(number_of_symbols, V8.NumberOfSymbols) \
/* Current amount of memory in external string buffers. */ \
SC(total_external_string_memory, V8.TotalExternalStringMemory) \
SC(script_wrappers, V8.ScriptWrappers) \ SC(script_wrappers, V8.ScriptWrappers) \
SC(call_initialize_stubs, V8.CallInitializeStubs) \ SC(call_initialize_stubs, V8.CallInitializeStubs) \
SC(call_premonomorphic_stubs, V8.CallPreMonomorphicStubs) \ SC(call_premonomorphic_stubs, V8.CallPreMonomorphicStubs) \
......
...@@ -447,6 +447,40 @@ THREADED_TEST(UsingExternalAsciiString) { ...@@ -447,6 +447,40 @@ THREADED_TEST(UsingExternalAsciiString) {
} }
THREADED_TEST(ScavengeExternalString) {
TestResource::dispose_count = 0;
{
v8::HandleScope scope;
uint16_t* two_byte_string = AsciiToTwoByteString("test string");
Local<String> string =
String::NewExternal(new TestResource(two_byte_string));
i::Handle<i::String> istring = v8::Utils::OpenHandle(*string);
i::Heap::CollectGarbage(0, i::NEW_SPACE);
CHECK(i::Heap::InNewSpace(*istring));
CHECK_EQ(0, TestResource::dispose_count);
}
i::Heap::CollectGarbage(0, i::NEW_SPACE);
CHECK_EQ(1, TestResource::dispose_count);
}
THREADED_TEST(ScavengeExternalAsciiString) {
TestAsciiResource::dispose_count = 0;
{
v8::HandleScope scope;
const char* one_byte_string = "test string";
Local<String> string = String::NewExternal(
new TestAsciiResource(i::StrDup(one_byte_string)));
i::Handle<i::String> istring = v8::Utils::OpenHandle(*string);
i::Heap::CollectGarbage(0, i::NEW_SPACE);
CHECK(i::Heap::InNewSpace(*istring));
CHECK_EQ(0, TestAsciiResource::dispose_count);
}
i::Heap::CollectGarbage(0, i::NEW_SPACE);
CHECK_EQ(1, TestAsciiResource::dispose_count);
}
THREADED_TEST(StringConcat) { THREADED_TEST(StringConcat) {
{ {
v8::HandleScope scope; v8::HandleScope scope;
......
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