From 122937fc67fc79732257b068dd5b6b140ca9b28c Mon Sep 17 00:00:00 2001 From: Anna Henningsen Date: Thu, 16 Apr 2020 17:46:07 +0200 Subject: [PATCH] deps: V8: cherry-pick 4b1447e4bb0e MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Original commit message: Improve V8 GC logic for external memory The logic for V8 GC normally only takes the external memory growth since last mark-compact into account. Unfortunately, the amount of external memory recorded at the end of MC is often too high. The reason is that it might take a while for the external memory associated with the GCed objects to be released (e.g. V8 itself post a task to release external memory for ArrayBuffer backing stores). In a worst case scenario GC is driven only by external memory and none of the external memory is released by the end of the MC. Then each MC will record the external memory at its highest point and the GC logic will allow the external memory to grow a bit higher each time which can lead to excessive memory use. This patch improves the situation a bit by calculating the growth from the lowest external memory seen since the last MC. That way the growth calculation will be offset from a level presumably closer to the intended one (to what it would have been if the external memory associated with the GCed objects was released during the MC). Now, this fix is not perfect because it can be thrown off by external memory growth occurring before the lingering memory is released. However, it seems to work rather well in practice (e.g. when playing MSE video on YT). Bug: v8:10185 Change-Id: Ifcdd87eb45f3ae4a99d2aeec667c3ae4ca9a52b6 Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2042711 Reviewed-by: Ulan Degenbaev Reviewed-by: Dominik Inführ Reviewed-by: Jakob Gruber Commit-Queue: Dominik Inführ Cr-Commit-Position: refs/heads/master@{#66193} Refs: https://github.com/v8/v8/commit/4b1447e4bb0e306c40214026342858664a13b551 PR-URL: https://github.com/nodejs/node/pull/32885 Reviewed-By: Michaël Zasso Reviewed-By: Ujjwal Sharma Reviewed-By: Gerhard Stöbich Reviewed-By: Beth Griggs --- common.gypi | 2 +- deps/v8/include/v8-internal.h | 4 ++-- deps/v8/include/v8.h | 27 +++++++++++++-------------- deps/v8/src/execution/isolate-data.h | 9 +++++---- deps/v8/src/execution/isolate.cc | 6 +++--- deps/v8/src/heap/heap-inl.h | 13 ++++++++++--- deps/v8/src/heap/heap.cc | 10 +++++----- 7 files changed, 39 insertions(+), 32 deletions(-) diff --git a/common.gypi b/common.gypi index fb364b55f5e63e..b23898301a4898 100644 --- a/common.gypi +++ b/common.gypi @@ -35,7 +35,7 @@ # Reset this number to 0 on major V8 upgrades. # Increment by one for each non-official patch applied to deps/v8. - 'v8_embedder_string': '-node.12', + 'v8_embedder_string': '-node.13', ##### V8 defaults for Node.js ##### diff --git a/deps/v8/include/v8-internal.h b/deps/v8/include/v8-internal.h index e4d698e6ce7e4c..876408ebba98f9 100644 --- a/deps/v8/include/v8-internal.h +++ b/deps/v8/include/v8-internal.h @@ -160,10 +160,10 @@ class Internals { kNumIsolateDataSlots * kApiSystemPointerSize; static const int kExternalMemoryLimitOffset = kExternalMemoryOffset + kApiInt64Size; - static const int kExternalMemoryAtLastMarkCompactOffset = + static const int kExternalMemoryLowSinceMarkCompactOffset = kExternalMemoryLimitOffset + kApiInt64Size; static const int kIsolateFastCCallCallerFpOffset = - kExternalMemoryAtLastMarkCompactOffset + kApiInt64Size; + kExternalMemoryLowSinceMarkCompactOffset + kApiInt64Size; static const int kIsolateFastCCallCallerPcOffset = kIsolateFastCCallCallerFpOffset + kApiSystemPointerSize; static const int kIsolateStackGuardOffset = diff --git a/deps/v8/include/v8.h b/deps/v8/include/v8.h index 54bc4f08359125..13724930103db1 100644 --- a/deps/v8/include/v8.h +++ b/deps/v8/include/v8.h @@ -11854,9 +11854,9 @@ int64_t Isolate::AdjustAmountOfExternalAllocatedMemory( reinterpret_cast(this) + I::kExternalMemoryOffset); int64_t* external_memory_limit = reinterpret_cast( reinterpret_cast(this) + I::kExternalMemoryLimitOffset); - int64_t* external_memory_at_last_mc = + int64_t* external_memory_low_since_mc = reinterpret_cast(reinterpret_cast(this) + - I::kExternalMemoryAtLastMarkCompactOffset); + I::kExternalMemoryLowSinceMarkCompactOffset); // Embedders are weird: we see both over- and underflows here. Perform the // addition with unsigned types to avoid undefined behavior. @@ -11865,23 +11865,22 @@ int64_t Isolate::AdjustAmountOfExternalAllocatedMemory( static_cast(*external_memory)); *external_memory = amount; - int64_t allocation_diff_since_last_mc = - static_cast(static_cast(*external_memory) - - static_cast(*external_memory_at_last_mc)); + if (amount < *external_memory_low_since_mc) { + *external_memory_low_since_mc = amount; + *external_memory_limit = amount + I::kExternalAllocationSoftLimit; + } + + if (change_in_bytes <= 0) return *external_memory; + + int64_t allocation_diff_since_last_mc = static_cast( + static_cast(*external_memory) - + static_cast(*external_memory_low_since_mc)); // Only check memory pressure and potentially trigger GC if the amount of // external memory increased. if (allocation_diff_since_last_mc > kMemoryReducerActivationLimit) { CheckMemoryPressure(); } - - if (change_in_bytes < 0) { - const int64_t lower_limit = - static_cast(static_cast(*external_memory_limit) + - static_cast(change_in_bytes)); - if (lower_limit > I::kExternalAllocationSoftLimit) { - *external_memory_limit = lower_limit; - } - } else if (change_in_bytes > 0 && amount > *external_memory_limit) { + if (amount > *external_memory_limit) { ReportExternalAllocationLimitReached(); } return *external_memory; diff --git a/deps/v8/src/execution/isolate-data.h b/deps/v8/src/execution/isolate-data.h index f402296ab54c22..c30c6eb80d3bc3 100644 --- a/deps/v8/src/execution/isolate-data.h +++ b/deps/v8/src/execution/isolate-data.h @@ -117,7 +117,7 @@ class IsolateData final { V(kEmbedderDataOffset, Internals::kNumIsolateDataSlots* kSystemPointerSize) \ V(kExternalMemoryOffset, kInt64Size) \ V(kExternalMemoryLlimitOffset, kInt64Size) \ - V(kExternalMemoryAtLastMarkCompactOffset, kInt64Size) \ + V(kExternalMemoryLowSinceMarkCompactOffset, kInt64Size) \ V(kFastCCallCallerFPOffset, kSystemPointerSize) \ V(kFastCCallCallerPCOffset, kSystemPointerSize) \ V(kStackGuardOffset, StackGuard::kSizeInBytes) \ @@ -151,7 +151,7 @@ class IsolateData final { int64_t external_memory_limit_ = kExternalAllocationSoftLimit; // Caches the amount of external memory registered at the last MC. - int64_t external_memory_at_last_mark_compact_ = 0; + int64_t external_memory_low_since_mark_compact_ = 0; // Stores the state of the caller for TurboAssembler::CallCFunction so that // the sampling CPU profiler can iterate the stack during such calls. These @@ -220,8 +220,9 @@ void IsolateData::AssertPredictableLayout() { kExternalMemoryOffset); STATIC_ASSERT(offsetof(IsolateData, external_memory_limit_) == kExternalMemoryLlimitOffset); - STATIC_ASSERT(offsetof(IsolateData, external_memory_at_last_mark_compact_) == - kExternalMemoryAtLastMarkCompactOffset); + STATIC_ASSERT( + offsetof(IsolateData, external_memory_low_since_mark_compact_) == + kExternalMemoryLowSinceMarkCompactOffset); STATIC_ASSERT(offsetof(IsolateData, fast_c_call_caller_fp_) == kFastCCallCallerFPOffset); STATIC_ASSERT(offsetof(IsolateData, fast_c_call_caller_pc_) == diff --git a/deps/v8/src/execution/isolate.cc b/deps/v8/src/execution/isolate.cc index 3da8963c53fb32..1bedc20822c08c 100644 --- a/deps/v8/src/execution/isolate.cc +++ b/deps/v8/src/execution/isolate.cc @@ -2900,10 +2900,10 @@ void Isolate::CheckIsolateLayout() { CHECK_EQ(static_cast( OFFSET_OF(Isolate, isolate_data_.external_memory_limit_)), Internals::kExternalMemoryLimitOffset); - CHECK_EQ(Internals::kExternalMemoryAtLastMarkCompactOffset % 8, 0); + CHECK_EQ(Internals::kExternalMemoryLowSinceMarkCompactOffset % 8, 0); CHECK_EQ(static_cast(OFFSET_OF( - Isolate, isolate_data_.external_memory_at_last_mark_compact_)), - Internals::kExternalMemoryAtLastMarkCompactOffset); + Isolate, isolate_data_.external_memory_low_since_mark_compact_)), + Internals::kExternalMemoryLowSinceMarkCompactOffset); } void Isolate::ClearSerializerData() { diff --git a/deps/v8/src/heap/heap-inl.h b/deps/v8/src/heap/heap-inl.h index 0e5230f1e05d22..0da0b35d392d8a 100644 --- a/deps/v8/src/heap/heap-inl.h +++ b/deps/v8/src/heap/heap-inl.h @@ -65,7 +65,14 @@ int64_t Heap::external_memory() { } void Heap::update_external_memory(int64_t delta) { - isolate()->isolate_data()->external_memory_ += delta; + const int64_t amount = isolate()->isolate_data()->external_memory_ + delta; + isolate()->isolate_data()->external_memory_ = amount; + if (amount < + isolate()->isolate_data()->external_memory_low_since_mark_compact_) { + isolate()->isolate_data()->external_memory_low_since_mark_compact_ = amount; + isolate()->isolate_data()->external_memory_limit_ = + amount + kExternalAllocationSoftLimit; + } } void Heap::update_external_memory_concurrently_freed(uintptr_t freed) { @@ -73,8 +80,8 @@ void Heap::update_external_memory_concurrently_freed(uintptr_t freed) { } void Heap::account_external_memory_concurrently_freed() { - isolate()->isolate_data()->external_memory_ -= - external_memory_concurrently_freed_; + update_external_memory( + -static_cast(external_memory_concurrently_freed_)); external_memory_concurrently_freed_ = 0; } diff --git a/deps/v8/src/heap/heap.cc b/deps/v8/src/heap/heap.cc index 28ff2e970d15a4..7e11c8626027d9 100644 --- a/deps/v8/src/heap/heap.cc +++ b/deps/v8/src/heap/heap.cc @@ -1452,7 +1452,7 @@ void Heap::ReportExternalMemoryPressure() { kGCCallbackFlagSynchronousPhantomCallbackProcessing | kGCCallbackFlagCollectAllExternalMemory); if (isolate()->isolate_data()->external_memory_ > - (isolate()->isolate_data()->external_memory_at_last_mark_compact_ + + (isolate()->isolate_data()->external_memory_low_since_mark_compact_ + external_memory_hard_limit())) { CollectAllGarbage( kReduceMemoryFootprintMask, @@ -2143,7 +2143,7 @@ void Heap::RecomputeLimits(GarbageCollector collector) { if (collector == MARK_COMPACTOR) { // Register the amount of external allocated memory. - isolate()->isolate_data()->external_memory_at_last_mark_compact_ = + isolate()->isolate_data()->external_memory_low_since_mark_compact_ = isolate()->isolate_data()->external_memory_; isolate()->isolate_data()->external_memory_limit_ = isolate()->isolate_data()->external_memory_ + @@ -4743,12 +4743,12 @@ size_t Heap::GlobalSizeOfObjects() { uint64_t Heap::PromotedExternalMemorySize() { IsolateData* isolate_data = isolate()->isolate_data(); if (isolate_data->external_memory_ <= - isolate_data->external_memory_at_last_mark_compact_) { + isolate_data->external_memory_low_since_mark_compact_) { return 0; } return static_cast( isolate_data->external_memory_ - - isolate_data->external_memory_at_last_mark_compact_); + isolate_data->external_memory_low_since_mark_compact_); } bool Heap::AllocationLimitOvershotByLargeMargin() { @@ -4871,7 +4871,7 @@ Heap::IncrementalMarkingLimit Heap::IncrementalMarkingLimitReached() { double gained_since_last_gc = PromotedSinceLastGC() + (isolate()->isolate_data()->external_memory_ - - isolate()->isolate_data()->external_memory_at_last_mark_compact_); + isolate()->isolate_data()->external_memory_low_since_mark_compact_); double size_before_gc = OldGenerationObjectsAndPromotedExternalMemorySize() - gained_since_last_gc;