Permalink
Browse files

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: https://v8.googlecode.com/svn/branches/bleeding_edge@3439 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
  • Loading branch information...
1 parent 3050b16 commit 8b34aaaefa3b447816e132cf9a02c642029f229a vitalyr@chromium.org committed Dec 9, 2009
Showing with 280 additions and 184 deletions.
  1. +16 −10 include/v8.h
  2. +4 −90 src/api.cc
  3. +6 −1 src/global-handles.cc
  4. +1 −1 src/globals.h
  5. +63 −0 src/heap-inl.h
  6. +107 −20 src/heap.cc
  7. +41 −1 src/heap.h
  8. +8 −59 src/mark-compact.cc
  9. +0 −2 src/v8-counters.h
  10. +34 −0 test/cctest/test-api.cc
View
@@ -833,13 +833,26 @@ class V8EXPORT String : public Primitive {
* Returns true if the string is both external and ascii
*/
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
* buffer that resides outside V8's heap. Implement an
* ExternalStringResource to manage the life cycle of the underlying
* buffer. Note that the string data must be immutable.
*/
- class V8EXPORT ExternalStringResource { // NOLINT
+ class V8EXPORT ExternalStringResource
+ : public ExternalStringResourceBase {
public:
/**
* Override the destructor to manage the life cycle of the underlying
@@ -852,10 +865,6 @@ class V8EXPORT String : public Primitive {
virtual size_t length() const = 0;
protected:
ExternalStringResource() {}
- private:
- // Disallow copying and assigning.
- ExternalStringResource(const ExternalStringResource&);
- void operator=(const ExternalStringResource&);
};
/**
@@ -869,7 +878,8 @@ class V8EXPORT String : public Primitive {
* Use String::New or convert to 16 bit data for non-ASCII.
*/
- class V8EXPORT ExternalAsciiStringResource { // NOLINT
+ class V8EXPORT ExternalAsciiStringResource
+ : public ExternalStringResourceBase {
public:
/**
* Override the destructor to manage the life cycle of the underlying
@@ -882,10 +892,6 @@ class V8EXPORT String : public Primitive {
virtual size_t length() const = 0;
protected:
ExternalAsciiStringResource() {}
- private:
- // Disallow copying and assigning.
- ExternalAsciiStringResource(const ExternalAsciiStringResource&);
- void operator=(const ExternalAsciiStringResource&);
};
/**
View
@@ -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(
v8::String::ExternalStringResource* resource) {
EnsureInitialized("v8::String::NewExternal()");
LOG_API("String::NewExternal");
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::Object> handle = i::GlobalHandles::Create(*result);
- i::GlobalHandles::MakeWeak(handle.location(),
- resource,
- &DisposeExternalString);
+ i::ExternalStringTable::AddString(*result);
return Utils::ToLocal(result);
}
@@ -3168,13 +3100,7 @@ bool v8::String::MakeExternal(v8::String::ExternalStringResource* resource) {
i::Handle<i::String> obj = Utils::OpenHandle(this);
bool result = obj->MakeExternal(resource);
if (result && !obj->IsSymbol()) {
- // Operation was successful and the string is not a symbol. In this case
- // 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);
+ i::ExternalStringTable::AddString(*obj);
}
return result;
}
@@ -3185,14 +3111,8 @@ Local<String> v8::String::NewExternal(
EnsureInitialized("v8::String::NewExternal()");
LOG_API("String::NewExternal");
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::Object> handle = i::GlobalHandles::Create(*result);
- i::GlobalHandles::MakeWeak(handle.location(),
- resource,
- &DisposeExternalAsciiString);
+ i::ExternalStringTable::AddString(*result);
return Utils::ToLocal(result);
}
@@ -3205,13 +3125,7 @@ bool v8::String::MakeExternal(
i::Handle<i::String> obj = Utils::OpenHandle(this);
bool result = obj->MakeExternal(resource);
if (result && !obj->IsSymbol()) {
- // Operation was successful and the string is not a symbol. In this case
- // 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);
+ i::ExternalStringTable::AddString(*obj);
}
return result;
}
View
@@ -168,6 +168,12 @@ class GlobalHandles::Node : public Malloced {
if (first_deallocated()) {
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.
VMState state(EXTERNAL);
func(object, par);
@@ -507,5 +513,4 @@ void GlobalHandles::RemoveObjectGroups() {
object_groups->Clear();
}
-
} } // namespace v8::internal
View
@@ -294,7 +294,7 @@ enum GarbageCollector { SCAVENGER, MARK_COMPACTOR };
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
View
@@ -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() {
#ifdef DEBUG
Counters::objs_since_last_full.Increment();
@@ -321,6 +334,56 @@ inline bool Heap::allow_allocation(bool new_state) {
#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
#endif // V8_HEAP_INL_H_
Oops, something went wrong.

0 comments on commit 8b34aaa

Please sign in to comment.