diff --git a/common.gypi b/common.gypi index a6896876d45e4c..479fd0e6d7d60f 100644 --- a/common.gypi +++ b/common.gypi @@ -38,7 +38,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.31', + 'v8_embedder_string': '-node.32', ##### V8 defaults for Node.js ##### diff --git a/deps/v8/include/v8-isolate.h b/deps/v8/include/v8-isolate.h index 332688e77b48e6..5f20c568608081 100644 --- a/deps/v8/include/v8-isolate.h +++ b/deps/v8/include/v8-isolate.h @@ -950,6 +950,13 @@ class V8_EXPORT Isolate { */ void GetHeapStatistics(HeapStatistics* heap_statistics); + /** + * Get total allocated bytes since isolate creation. + * This should be used only by Node.JS, since it's a temporary method + * to avoid breaking ABI on HeapStatistics. + */ + uint64_t GetTotalAllocatedBytes(); + /** * Returns the number of spaces in the heap. */ diff --git a/deps/v8/src/api/api.cc b/deps/v8/src/api/api.cc index ba759168aa92f5..71c07e78865efc 100644 --- a/deps/v8/src/api/api.cc +++ b/deps/v8/src/api/api.cc @@ -10363,6 +10363,11 @@ void Isolate::GetHeapStatistics(HeapStatistics* heap_statistics) { #endif // V8_ENABLE_WEBASSEMBLY } +uint64_t Isolate::GetTotalAllocatedBytes() { + i::Isolate* i_isolate = reinterpret_cast(this); + return i_isolate->heap()->GetTotalAllocatedBytes(); +} + size_t Isolate::NumberOfHeapSpaces() { return i::LAST_SPACE - i::FIRST_SPACE + 1; } diff --git a/deps/v8/src/heap/heap-allocator.cc b/deps/v8/src/heap/heap-allocator.cc index 6f5946fc2374c3..88491df2ce2388 100644 --- a/deps/v8/src/heap/heap-allocator.cc +++ b/deps/v8/src/heap/heap-allocator.cc @@ -65,24 +65,42 @@ AllocationResult HeapAllocator::AllocateRawLargeInternal( int size_in_bytes, AllocationType allocation, AllocationOrigin origin, AllocationAlignment alignment) { DCHECK_GT(size_in_bytes, heap_->MaxRegularHeapObjectSize(allocation)); + AllocationResult allocation_result; switch (allocation) { case AllocationType::kYoung: - return new_lo_space()->AllocateRaw(local_heap_, size_in_bytes); + allocation_result = + new_lo_space()->AllocateRaw(local_heap_, size_in_bytes); + break; case AllocationType::kOld: - return lo_space()->AllocateRaw(local_heap_, size_in_bytes); + allocation_result = + lo_space()->AllocateRaw(local_heap_, size_in_bytes); + break; case AllocationType::kCode: - return code_lo_space()->AllocateRaw(local_heap_, size_in_bytes); + allocation_result = + code_lo_space()->AllocateRaw(local_heap_, size_in_bytes); + break; case AllocationType::kSharedOld: - return shared_lo_space()->AllocateRaw(local_heap_, size_in_bytes); + allocation_result = + shared_lo_space()->AllocateRaw(local_heap_, size_in_bytes); + break; case AllocationType::kTrusted: - return trusted_lo_space()->AllocateRaw(local_heap_, size_in_bytes); + allocation_result = + trusted_lo_space()->AllocateRaw(local_heap_, size_in_bytes); + break; case AllocationType::kSharedTrusted: - return shared_trusted_lo_space()->AllocateRaw(local_heap_, size_in_bytes); + allocation_result = shared_trusted_lo_space()->AllocateRaw( + local_heap_, size_in_bytes); + break; case AllocationType::kMap: case AllocationType::kReadOnly: case AllocationType::kSharedMap: UNREACHABLE(); } + if (!allocation_result.IsFailure()) { + int allocated_size = ALIGN_TO_ALLOCATION_ALIGNMENT(size_in_bytes); + heap_->AddTotalAllocatedBytes(allocated_size); + } + return allocation_result; } namespace { diff --git a/deps/v8/src/heap/heap.cc b/deps/v8/src/heap/heap.cc index f4e94e406e2690..392edeec5e84a6 100644 --- a/deps/v8/src/heap/heap.cc +++ b/deps/v8/src/heap/heap.cc @@ -7446,6 +7446,10 @@ int Heap::NextStackTraceId() { return last_id; } +uint64_t Heap::GetTotalAllocatedBytes() { + return total_allocated_bytes_.load(std::memory_order_relaxed); +} + EmbedderStackStateScope::EmbedderStackStateScope( Heap* heap, EmbedderStackStateOrigin origin, StackState stack_state) : heap_(heap), diff --git a/deps/v8/src/heap/heap.h b/deps/v8/src/heap/heap.h index f2c5a25010d2a8..ef4de1c4117947 100644 --- a/deps/v8/src/heap/heap.h +++ b/deps/v8/src/heap/heap.h @@ -1635,6 +1635,11 @@ class Heap final { bool ShouldUseBackgroundThreads() const; bool ShouldUseIncrementalMarking() const; + void AddTotalAllocatedBytes(size_t size) { + total_allocated_bytes_.fetch_add(size, std::memory_order_relaxed); + } + uint64_t GetTotalAllocatedBytes(); + HeapAllocator* allocator() { return heap_allocator_; } const HeapAllocator* allocator() const { return heap_allocator_; } @@ -2409,6 +2414,8 @@ class Heap final { // actually finished. bool is_full_gc_during_loading_ = false; + std::atomic total_allocated_bytes_ = 0; + // Classes in "heap" can be friends. friend class ActivateMemoryReducerTask; friend class AlwaysAllocateScope; diff --git a/deps/v8/src/heap/main-allocator.cc b/deps/v8/src/heap/main-allocator.cc index 375cc521989352..d4040f183f60cd 100644 --- a/deps/v8/src/heap/main-allocator.cc +++ b/deps/v8/src/heap/main-allocator.cc @@ -298,6 +298,12 @@ void MainAllocator::ResetLab(Address start, Address end, Address extended_end) { MemoryChunkMetadata::UpdateHighWaterMark(top()); } + // This is going to overestimate a bit of the total allocated bytes, since the + // LAB was not used yet. However the leftover compared to the LAB itself is + // quite small, so it seems tolerable. + if (local_heap_) { + local_heap_->heap()->AddTotalAllocatedBytes(end - start); + } allocation_info().Reset(start, end); if (SupportsPendingAllocation()) { diff --git a/doc/api/v8.md b/doc/api/v8.md index 3b0795bba8e648..9b038cce419c4f 100644 --- a/doc/api/v8.md +++ b/doc/api/v8.md @@ -197,6 +197,7 @@ Returns an object with the following properties: * `total_global_handles_size` {number} * `used_global_handles_size` {number} * `external_memory` {number} +* `total_allocated_bytes` {number} `total_heap_size` The value of total\_heap\_size is the number of bytes V8 has allocated for the heap. This can grow if used\_heap needs more memory. @@ -250,6 +251,9 @@ used memory size of V8 global handles. `external_memory` The value of external\_memory is the memory size of array buffers and external strings. +`total_allocated_bytes` The value of total allocated bytes since the Isolate +creation. + ```js @@ -267,7 +271,8 @@ buffers and external strings. number_of_detached_contexts: 0, total_global_handles_size: 8192, used_global_handles_size: 3296, - external_memory: 318824 + external_memory: 318824, + total_allocated_bytes: 24970208 } ``` diff --git a/lib/v8.js b/lib/v8.js index 47f694103719aa..7f1789f1e04f99 100644 --- a/lib/v8.js +++ b/lib/v8.js @@ -117,6 +117,7 @@ const { stopCpuProfile: _stopCpuProfile, isStringOneByteRepresentation: _isStringOneByteRepresentation, updateHeapStatisticsBuffer, + getTotalAllocatedBytes, updateHeapSpaceStatisticsBuffer, updateHeapCodeStatisticsBuffer, setHeapSnapshotNearHeapLimit: _setHeapSnapshotNearHeapLimit, @@ -246,6 +247,7 @@ function getHeapStatistics() { total_global_handles_size: buffer[kTotalGlobalHandlesSizeIndex], used_global_handles_size: buffer[kUsedGlobalHandlesSizeIndex], external_memory: buffer[kExternalMemoryIndex], + total_allocated_bytes: getTotalAllocatedBytes(), }; } diff --git a/src/node_v8.cc b/src/node_v8.cc index 8dd32dad262679..ac818812ccb84f 100644 --- a/src/node_v8.cc +++ b/src/node_v8.cc @@ -212,6 +212,12 @@ void UpdateHeapStatisticsBuffer(const FunctionCallbackInfo& args) { #undef V } +void GetTotalAllocatedBytes(const FunctionCallbackInfo& args) { + Isolate* isolate = args.GetIsolate(); + uint64_t allocated_bytes = isolate->GetTotalAllocatedBytes(); + args.GetReturnValue().Set(Number::New(isolate, allocated_bytes)); +} + void UpdateHeapSpaceStatisticsBuffer(const FunctionCallbackInfo& args) { BindingData* data = Realm::GetBindingData(args); @@ -692,6 +698,11 @@ void Initialize(Local target, "updateHeapStatisticsBuffer", UpdateHeapStatisticsBuffer); + SetMethod(context, + target, + "getTotalAllocatedBytes", + GetTotalAllocatedBytes); + SetMethod(context, target, "updateHeapCodeStatisticsBuffer", @@ -773,6 +784,7 @@ void Initialize(Local target, void RegisterExternalReferences(ExternalReferenceRegistry* registry) { registry->Register(CachedDataVersionTag); registry->Register(UpdateHeapStatisticsBuffer); + registry->Register(GetTotalAllocatedBytes); registry->Register(UpdateHeapCodeStatisticsBuffer); registry->Register(UpdateHeapSpaceStatisticsBuffer); registry->Register(SetFlagsFromString); diff --git a/src/node_worker.cc b/src/node_worker.cc index 7bae29747d8cd8..f09561f77e3a91 100644 --- a/src/node_worker.cc +++ b/src/node_worker.cc @@ -1263,6 +1263,7 @@ void Worker::GetHeapStatistics(const FunctionCallbackInfo& args) { "total_global_handles_size", "used_global_handles_size", "external_memory", + "total_allocated_bytes", }; tmpl = DictionaryTemplate::New(isolate, heap_stats_names); env->set_heap_statistics_template(tmpl); @@ -1283,7 +1284,8 @@ void Worker::GetHeapStatistics(const FunctionCallbackInfo& args) { Number::New(isolate, heap_stats->number_of_detached_contexts()), Number::New(isolate, heap_stats->total_global_handles_size()), Number::New(isolate, heap_stats->used_global_handles_size()), - Number::New(isolate, heap_stats->external_memory())}; + Number::New(isolate, heap_stats->external_memory()), + Number::New(isolate, isolate->GetTotalAllocatedBytes())}; Local obj; if (!NewDictionaryInstanceNullProto( diff --git a/test/parallel/test-v8-stats.js b/test/parallel/test-v8-stats.js index 07be833e6e749a..5ee4c5aeb31adf 100644 --- a/test/parallel/test-v8-stats.js +++ b/test/parallel/test-v8-stats.js @@ -12,6 +12,7 @@ const keys = [ 'number_of_detached_contexts', 'number_of_native_contexts', 'peak_malloced_memory', + 'total_allocated_bytes', 'total_available_size', 'total_global_handles_size', 'total_heap_size', diff --git a/test/parallel/test-worker-heap-statistics.js b/test/parallel/test-worker-heap-statistics.js index 12a748c303a026..ba3165aa24aba0 100644 --- a/test/parallel/test-worker-heap-statistics.js +++ b/test/parallel/test-worker-heap-statistics.js @@ -40,6 +40,7 @@ if (isMainThread) { `total_global_handles_size`, `used_global_handles_size`, `external_memory`, + `total_allocated_bytes`, ].sort(); assert.deepStrictEqual(keys, Object.keys(stats).sort()); for (const key of keys) {