Commit 5863c0b6 authored by Rodrigo Bruno's avatar Rodrigo Bruno Committed by Commit Bot

[heap] Added External Strings to external memory accounting.

Bug: chromium:845409
Change-Id: I3fe2b294f6e038d77787cf0870d244ba7cc20550
Reviewed-on: https://chromium-review.googlesource.com/1118164
Commit-Queue: Rodrigo Bruno <rfbpb@google.com>
Reviewed-by: 's avatarUlan Degenbaev <ulan@chromium.org>
Cr-Commit-Position: refs/heads/master@{#54110}
parent 327668d5
......@@ -301,12 +301,22 @@ void Heap::UpdateAllocationsHash(uint32_t value) {
void Heap::RegisterExternalString(String* string) {
DCHECK(string->IsExternalString());
external_string_table_.AddString(string);
Page* page = Page::FromHeapObject(string);
ExternalString* ext_string = ExternalString::cast(string);
page->IncrementExternalBackingStoreBytes(
ExternalBackingStoreType::kExternalString,
ext_string->ExternalPayloadSize());
}
void Heap::FinalizeExternalString(String* string) {
DCHECK(string->IsExternalString());
Page* page = Page::FromHeapObject(string);
ExternalString* ext_string = ExternalString::cast(string);
v8::String::ExternalStringResourceBase** resource_addr =
reinterpret_cast<v8::String::ExternalStringResourceBase**>(
reinterpret_cast<byte*>(string) + ExternalString::kResourceOffset -
......@@ -314,6 +324,10 @@ void Heap::FinalizeExternalString(String* string) {
// Dispose of the C++ object if it has not already been disposed.
if (*resource_addr != nullptr) {
page->DecrementExternalBackingStoreBytes(
ExternalBackingStoreType::kExternalString,
ext_string->ExternalPayloadSize());
(*resource_addr)->Dispose();
*resource_addr = nullptr;
}
......
......@@ -2304,6 +2304,16 @@ void Heap::ProtectUnprotectedMemoryChunks() {
unprotected_memory_chunks_.clear();
}
void Heap::ProcessMovedExternalString(Page* old_page, Page* new_page,
String* string) {
DCHECK(string->IsExternalString());
size_t size = ExternalString::cast(string)->ExternalPayloadSize();
old_page->DecrementExternalBackingStoreBytes(
ExternalBackingStoreType::kExternalString, size);
new_page->IncrementExternalBackingStoreBytes(
ExternalBackingStoreType::kExternalString, size);
}
String* Heap::UpdateNewSpaceReferenceInExternalStringTableEntry(Heap* heap,
Object** p) {
MapWord first_word = HeapObject::cast(*p)->map_word();
......@@ -2321,10 +2331,18 @@ String* Heap::UpdateNewSpaceReferenceInExternalStringTableEntry(Heap* heap,
}
// String is still reachable.
String* string = String::cast(first_word.ToForwardingAddress());
if (string->IsThinString()) string = ThinString::cast(string)->actual();
String* new_string = String::cast(first_word.ToForwardingAddress());
if (new_string->IsThinString()) {
new_string = ThinString::cast(new_string)->actual();
} else if (new_string->IsExternalString()) {
heap->ProcessMovedExternalString(
Page::FromAddress(reinterpret_cast<Address>(*p)),
Page::FromHeapObject(new_string), new_string);
return new_string;
}
// Internalization can replace external strings with non-external strings.
return string->IsExternalString() ? string : nullptr;
return new_string->IsExternalString() ? new_string : nullptr;
}
void Heap::ExternalStringTable::Verify() {
......
......@@ -929,6 +929,9 @@ class Heap {
external_memory_concurrently_freed_ = 0;
}
void ProcessMovedExternalString(Page* old_page, Page* new_page,
String* string);
void CompactFixedArraysOfWeakCells();
void AddRetainedMap(Handle<Map> map);
......
......@@ -2186,7 +2186,14 @@ static String* UpdateReferenceInExternalStringTableEntry(Heap* heap,
MapWord map_word = HeapObject::cast(*p)->map_word();
if (map_word.IsForwardingAddress()) {
return String::cast(map_word.ToForwardingAddress());
String* new_string = String::cast(map_word.ToForwardingAddress());
if (new_string->IsExternalString()) {
heap->ProcessMovedExternalString(
Page::FromAddress(reinterpret_cast<Address>(*p)),
Page::FromHeapObject(new_string), new_string);
}
return new_string;
}
return String::cast(*p);
......
......@@ -756,6 +756,10 @@ class Page : public MemoryChunk {
static Page* FromAddress(Address addr) {
return reinterpret_cast<Page*>(OffsetFrom(addr) & ~kPageAlignmentMask);
}
static Page* FromHeapObject(const HeapObject* o) {
return reinterpret_cast<Page*>(reinterpret_cast<Address>(o) &
~kAlignmentMask);
}
// Returns the page containing the address provided. The address can
// potentially point righter after the page. To be also safe for tagged values
......
......@@ -117,6 +117,7 @@ v8_source_set("cctest_sources") {
"heap/test-compaction.cc",
"heap/test-concurrent-marking.cc",
"heap/test-embedder-tracing.cc",
"heap/test-external-string-tracker.cc",
"heap/test-heap.cc",
"heap/test-incremental-marking.cc",
"heap/test-invalidated-slots.cc",
......
// Copyright 2016 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/api.h"
#include "src/heap/spaces.h"
#include "src/isolate.h"
#include "src/objects-inl.h"
#include "test/cctest/cctest.h"
#include "test/cctest/heap/heap-tester.h"
#include "test/cctest/heap/heap-utils.h"
#define TEST_STR "tests are great!"
namespace v8 {
namespace internal {
namespace heap {
// Adapted from cctest/test-api.cc
class TestOneByteResource : public v8::String::ExternalOneByteStringResource {
public:
explicit TestOneByteResource(const char* data, int* counter = nullptr,
size_t offset = 0)
: orig_data_(data),
data_(data + offset),
length_(strlen(data) - offset),
counter_(counter) {}
~TestOneByteResource() {
i::DeleteArray(orig_data_);
if (counter_ != nullptr) ++*counter_;
}
const char* data() const { return data_; }
size_t length() const { return length_; }
private:
const char* orig_data_;
const char* data_;
size_t length_;
int* counter_;
};
TEST(ExternalString_ExternalBackingStoreSizeIncreases) {
CcTest::InitializeVM();
LocalContext env;
v8::Isolate* isolate = env->GetIsolate();
Heap* heap = reinterpret_cast<Isolate*>(isolate)->heap();
ExternalBackingStoreType type = ExternalBackingStoreType::kExternalString;
const size_t backing_store_before =
heap->old_space()->ExternalBackingStoreBytes(type);
{
v8::HandleScope handle_scope(isolate);
v8::Local<v8::String> es = v8::String::NewExternal(
isolate, new TestOneByteResource(i::StrDup(TEST_STR)));
USE(es);
const size_t backing_store_after =
heap->old_space()->ExternalBackingStoreBytes(type);
CHECK_EQ(es->Length(), backing_store_after - backing_store_before);
}
}
TEST(ExternalString_ExternalBackingStoreSizeDecreases) {
ManualGCScope manual_gc_scope;
CcTest::InitializeVM();
LocalContext env;
v8::Isolate* isolate = env->GetIsolate();
Heap* heap = reinterpret_cast<Isolate*>(isolate)->heap();
ExternalBackingStoreType type = ExternalBackingStoreType::kExternalString;
const size_t backing_store_before =
heap->old_space()->ExternalBackingStoreBytes(type);
{
v8::HandleScope handle_scope(isolate);
v8::Local<v8::String> es = v8::String::NewExternal(
isolate, new TestOneByteResource(i::StrDup(TEST_STR)));
USE(es);
}
heap::GcAndSweep(heap, OLD_SPACE);
const size_t backing_store_after =
heap->old_space()->ExternalBackingStoreBytes(type);
CHECK_EQ(0, backing_store_after - backing_store_before);
}
TEST(ExternalString_ExternalBackingStoreSizeIncreasesMarkCompact) {
if (FLAG_never_compact) return;
ManualGCScope manual_gc_scope;
FLAG_manual_evacuation_candidates_selection = true;
CcTest::InitializeVM();
LocalContext env;
v8::Isolate* isolate = env->GetIsolate();
Heap* heap = reinterpret_cast<Isolate*>(isolate)->heap();
heap::AbandonCurrentlyFreeMemory(heap->old_space());
ExternalBackingStoreType type = ExternalBackingStoreType::kExternalString;
const size_t backing_store_before =
heap->old_space()->ExternalBackingStoreBytes(type);
{
v8::HandleScope handle_scope(isolate);
v8::Local<v8::String> es = v8::String::NewExternal(
isolate, new TestOneByteResource(i::StrDup(TEST_STR)));
Handle<String> esh = v8::Utils::OpenHandle(*es);
Page* page_before_gc = Page::FromAddress(esh->address());
heap::ForceEvacuationCandidate(page_before_gc);
CcTest::CollectAllGarbage();
const size_t backing_store_after =
heap->old_space()->ExternalBackingStoreBytes(type);
CHECK_EQ(es->Length(), backing_store_after - backing_store_before);
}
heap::GcAndSweep(heap, OLD_SPACE);
const size_t backing_store_after =
heap->old_space()->ExternalBackingStoreBytes(type);
CHECK_EQ(0, backing_store_after - backing_store_before);
}
TEST(ExternalString_ExternalBackingStoreSizeIncreasesAfterExternalization) {
CcTest::InitializeVM();
LocalContext env;
v8::Isolate* isolate = env->GetIsolate();
Heap* heap = reinterpret_cast<Isolate*>(isolate)->heap();
ExternalBackingStoreType type = ExternalBackingStoreType::kExternalString;
size_t old_backing_store_before = 0, new_backing_store_before = 0;
{
v8::HandleScope handle_scope(isolate);
new_backing_store_before =
heap->new_space()->ExternalBackingStoreBytes(type);
old_backing_store_before =
heap->old_space()->ExternalBackingStoreBytes(type);
// Allocate normal string in the new gen.
v8::Local<v8::String> str =
v8::String::NewFromUtf8(isolate, TEST_STR, v8::NewStringType::kNormal)
.ToLocalChecked();
CHECK_EQ(0, heap->new_space()->ExternalBackingStoreBytes(type) -
new_backing_store_before);
// Trigger GCs so that the newly allocated string moves to old gen.
heap::GcAndSweep(heap, NEW_SPACE); // in survivor space now
heap::GcAndSweep(heap, NEW_SPACE); // in old gen now
bool success =
str->MakeExternal(new TestOneByteResource(i::StrDup(TEST_STR)));
CHECK(success);
CHECK_EQ(str->Length(), heap->old_space()->ExternalBackingStoreBytes(type) -
old_backing_store_before);
}
heap::GcAndSweep(heap, OLD_SPACE);
CHECK_EQ(0, heap->old_space()->ExternalBackingStoreBytes(type) -
old_backing_store_before);
}
} // namespace heap
} // namespace internal
} // namespace v8
#undef TEST_STR
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