process: report ArrayBuffer memory in memoryUsage()
Report memory allocations performed by the `ArrayBuffer::Allocator`. PR-URL: https://github.com/nodejs/node/pull/31550 Reviewed-By: Richard Lau <riclau@uk.ibm.com> Reviewed-By: Colin Ihrig <cjihrig@gmail.com> Reviewed-By: Gus Caplan <me@gus.host> Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl> Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Rich Trott <rtrott@gmail.com> Reviewed-By: Gireesh Punathil <gpunathi@in.ibm.com>
This commit is contained in:
parent
9225939528
commit
abe6a2e3d1
@ -1510,6 +1510,9 @@ is no entry script.
|
|||||||
<!-- YAML
|
<!-- YAML
|
||||||
added: v0.1.16
|
added: v0.1.16
|
||||||
changes:
|
changes:
|
||||||
|
- version: REPLACEME
|
||||||
|
pr-url: https://github.com/nodejs/node/pull/31550
|
||||||
|
description: Added `arrayBuffers` to the returned object.
|
||||||
- version: v7.2.0
|
- version: v7.2.0
|
||||||
pr-url: https://github.com/nodejs/node/pull/9587
|
pr-url: https://github.com/nodejs/node/pull/9587
|
||||||
description: Added `external` to the returned object.
|
description: Added `external` to the returned object.
|
||||||
@ -1520,6 +1523,7 @@ changes:
|
|||||||
* `heapTotal` {integer}
|
* `heapTotal` {integer}
|
||||||
* `heapUsed` {integer}
|
* `heapUsed` {integer}
|
||||||
* `external` {integer}
|
* `external` {integer}
|
||||||
|
* `arrayBuffers` {integer}
|
||||||
|
|
||||||
The `process.memoryUsage()` method returns an object describing the memory usage
|
The `process.memoryUsage()` method returns an object describing the memory usage
|
||||||
of the Node.js process measured in bytes.
|
of the Node.js process measured in bytes.
|
||||||
@ -1538,19 +1542,22 @@ Will generate:
|
|||||||
rss: 4935680,
|
rss: 4935680,
|
||||||
heapTotal: 1826816,
|
heapTotal: 1826816,
|
||||||
heapUsed: 650472,
|
heapUsed: 650472,
|
||||||
external: 49879
|
external: 49879,
|
||||||
|
arrayBuffers: 9386
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
`heapTotal` and `heapUsed` refer to V8's memory usage.
|
* `heapTotal` and `heapUsed` refer to V8's memory usage.
|
||||||
`external` refers to the memory usage of C++ objects bound to JavaScript
|
* `external` refers to the memory usage of C++ objects bound to JavaScript
|
||||||
objects managed by V8. `rss`, Resident Set Size, is the amount of space
|
objects managed by V8.
|
||||||
occupied in the main memory device (that is a subset of the total allocated
|
* `rss`, Resident Set Size, is the amount of space occupied in the main
|
||||||
memory) for the process, which includes the _heap_, _code segment_ and _stack_.
|
memory device (that is a subset of the total allocated memory) for the
|
||||||
|
process, including all C++ and JavaScript objects and code.
|
||||||
The _heap_ is where objects, strings, and closures are stored. Variables are
|
* `arrayBuffers` refers to memory allocated for `ArrayBuffer`s and
|
||||||
stored in the _stack_ and the actual JavaScript code resides in the
|
`SharedArrayBuffer`s, including all Node.js [`Buffer`][]s.
|
||||||
_code segment_.
|
This is also included in the `external` value. When Node.js is used as an
|
||||||
|
embedded library, this value may be `0` because allocations for `ArrayBuffer`s
|
||||||
|
may not be tracked in that case.
|
||||||
|
|
||||||
When using [`Worker`][] threads, `rss` will be a value that is valid for the
|
When using [`Worker`][] threads, `rss` will be a value that is valid for the
|
||||||
entire process, while the other fields will only refer to the current thread.
|
entire process, while the other fields will only refer to the current thread.
|
||||||
@ -2518,6 +2525,7 @@ cases:
|
|||||||
[`'exit'`]: #process_event_exit
|
[`'exit'`]: #process_event_exit
|
||||||
[`'message'`]: child_process.html#child_process_event_message
|
[`'message'`]: child_process.html#child_process_event_message
|
||||||
[`'uncaughtException'`]: #process_event_uncaughtexception
|
[`'uncaughtException'`]: #process_event_uncaughtexception
|
||||||
|
[`Buffer`]: buffer.html
|
||||||
[`ChildProcess.disconnect()`]: child_process.html#child_process_subprocess_disconnect
|
[`ChildProcess.disconnect()`]: child_process.html#child_process_subprocess_disconnect
|
||||||
[`ChildProcess.send()`]: child_process.html#child_process_subprocess_send_message_sendhandle_options_callback
|
[`ChildProcess.send()`]: child_process.html#child_process_subprocess_send_message_sendhandle_options_callback
|
||||||
[`ChildProcess`]: child_process.html#child_process_class_childprocess
|
[`ChildProcess`]: child_process.html#child_process_class_childprocess
|
||||||
|
@ -146,14 +146,15 @@ function wrapProcessMethods(binding) {
|
|||||||
return hrBigintValues[0];
|
return hrBigintValues[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
const memValues = new Float64Array(4);
|
const memValues = new Float64Array(5);
|
||||||
function memoryUsage() {
|
function memoryUsage() {
|
||||||
_memoryUsage(memValues);
|
_memoryUsage(memValues);
|
||||||
return {
|
return {
|
||||||
rss: memValues[0],
|
rss: memValues[0],
|
||||||
heapTotal: memValues[1],
|
heapTotal: memValues[1],
|
||||||
heapUsed: memValues[2],
|
heapUsed: memValues[2],
|
||||||
external: memValues[3]
|
external: memValues[3],
|
||||||
|
arrayBuffers: memValues[4]
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -87,10 +87,34 @@ static void HostCleanupFinalizationGroupCallback(
|
|||||||
}
|
}
|
||||||
|
|
||||||
void* NodeArrayBufferAllocator::Allocate(size_t size) {
|
void* NodeArrayBufferAllocator::Allocate(size_t size) {
|
||||||
|
void* ret;
|
||||||
if (zero_fill_field_ || per_process::cli_options->zero_fill_all_buffers)
|
if (zero_fill_field_ || per_process::cli_options->zero_fill_all_buffers)
|
||||||
return UncheckedCalloc(size);
|
ret = UncheckedCalloc(size);
|
||||||
else
|
else
|
||||||
return UncheckedMalloc(size);
|
ret = UncheckedMalloc(size);
|
||||||
|
if (LIKELY(ret != nullptr))
|
||||||
|
total_mem_usage_.fetch_add(size, std::memory_order_relaxed);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
void* NodeArrayBufferAllocator::AllocateUninitialized(size_t size) {
|
||||||
|
void* ret = node::UncheckedMalloc(size);
|
||||||
|
if (LIKELY(ret != nullptr))
|
||||||
|
total_mem_usage_.fetch_add(size, std::memory_order_relaxed);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
void* NodeArrayBufferAllocator::Reallocate(
|
||||||
|
void* data, size_t old_size, size_t size) {
|
||||||
|
void* ret = UncheckedRealloc<char>(static_cast<char*>(data), size);
|
||||||
|
if (LIKELY(ret != nullptr) || UNLIKELY(size == 0))
|
||||||
|
total_mem_usage_.fetch_add(size - old_size, std::memory_order_relaxed);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
void NodeArrayBufferAllocator::Free(void* data, size_t size) {
|
||||||
|
total_mem_usage_.fetch_sub(size, std::memory_order_relaxed);
|
||||||
|
free(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
DebuggingArrayBufferAllocator::~DebuggingArrayBufferAllocator() {
|
DebuggingArrayBufferAllocator::~DebuggingArrayBufferAllocator() {
|
||||||
@ -140,11 +164,13 @@ void* DebuggingArrayBufferAllocator::Reallocate(void* data,
|
|||||||
|
|
||||||
void DebuggingArrayBufferAllocator::RegisterPointer(void* data, size_t size) {
|
void DebuggingArrayBufferAllocator::RegisterPointer(void* data, size_t size) {
|
||||||
Mutex::ScopedLock lock(mutex_);
|
Mutex::ScopedLock lock(mutex_);
|
||||||
|
NodeArrayBufferAllocator::RegisterPointer(data, size);
|
||||||
RegisterPointerInternal(data, size);
|
RegisterPointerInternal(data, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
void DebuggingArrayBufferAllocator::UnregisterPointer(void* data, size_t size) {
|
void DebuggingArrayBufferAllocator::UnregisterPointer(void* data, size_t size) {
|
||||||
Mutex::ScopedLock lock(mutex_);
|
Mutex::ScopedLock lock(mutex_);
|
||||||
|
NodeArrayBufferAllocator::UnregisterPointer(data, size);
|
||||||
UnregisterPointerInternal(data, size);
|
UnregisterPointerInternal(data, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -109,20 +109,24 @@ class NodeArrayBufferAllocator : public ArrayBufferAllocator {
|
|||||||
inline uint32_t* zero_fill_field() { return &zero_fill_field_; }
|
inline uint32_t* zero_fill_field() { return &zero_fill_field_; }
|
||||||
|
|
||||||
void* Allocate(size_t size) override; // Defined in src/node.cc
|
void* Allocate(size_t size) override; // Defined in src/node.cc
|
||||||
void* AllocateUninitialized(size_t size) override
|
void* AllocateUninitialized(size_t size) override;
|
||||||
{ return node::UncheckedMalloc(size); }
|
void Free(void* data, size_t size) override;
|
||||||
void Free(void* data, size_t) override { free(data); }
|
virtual void* Reallocate(void* data, size_t old_size, size_t size);
|
||||||
virtual void* Reallocate(void* data, size_t old_size, size_t size) {
|
virtual void RegisterPointer(void* data, size_t size) {
|
||||||
return static_cast<void*>(
|
total_mem_usage_.fetch_add(size, std::memory_order_relaxed);
|
||||||
UncheckedRealloc<char>(static_cast<char*>(data), size));
|
}
|
||||||
|
virtual void UnregisterPointer(void* data, size_t size) {
|
||||||
|
total_mem_usage_.fetch_sub(size, std::memory_order_relaxed);
|
||||||
}
|
}
|
||||||
virtual void RegisterPointer(void* data, size_t size) {}
|
|
||||||
virtual void UnregisterPointer(void* data, size_t size) {}
|
|
||||||
|
|
||||||
NodeArrayBufferAllocator* GetImpl() final { return this; }
|
NodeArrayBufferAllocator* GetImpl() final { return this; }
|
||||||
|
inline uint64_t total_mem_usage() const {
|
||||||
|
return total_mem_usage_.load(std::memory_order_relaxed);
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
uint32_t zero_fill_field_ = 1; // Boolean but exposed as uint32 to JS land.
|
uint32_t zero_fill_field_ = 1; // Boolean but exposed as uint32 to JS land.
|
||||||
|
std::atomic<size_t> total_mem_usage_ {0};
|
||||||
};
|
};
|
||||||
|
|
||||||
class DebuggingArrayBufferAllocator final : public NodeArrayBufferAllocator {
|
class DebuggingArrayBufferAllocator final : public NodeArrayBufferAllocator {
|
||||||
|
@ -200,10 +200,13 @@ static void MemoryUsage(const FunctionCallbackInfo<Value>& args) {
|
|||||||
HeapStatistics v8_heap_stats;
|
HeapStatistics v8_heap_stats;
|
||||||
isolate->GetHeapStatistics(&v8_heap_stats);
|
isolate->GetHeapStatistics(&v8_heap_stats);
|
||||||
|
|
||||||
|
NodeArrayBufferAllocator* array_buffer_allocator =
|
||||||
|
env->isolate_data()->node_allocator();
|
||||||
|
|
||||||
// Get the double array pointer from the Float64Array argument.
|
// Get the double array pointer from the Float64Array argument.
|
||||||
CHECK(args[0]->IsFloat64Array());
|
CHECK(args[0]->IsFloat64Array());
|
||||||
Local<Float64Array> array = args[0].As<Float64Array>();
|
Local<Float64Array> array = args[0].As<Float64Array>();
|
||||||
CHECK_EQ(array->Length(), 4);
|
CHECK_EQ(array->Length(), 5);
|
||||||
Local<ArrayBuffer> ab = array->Buffer();
|
Local<ArrayBuffer> ab = array->Buffer();
|
||||||
double* fields = static_cast<double*>(ab->GetBackingStore()->Data());
|
double* fields = static_cast<double*>(ab->GetBackingStore()->Data());
|
||||||
|
|
||||||
@ -211,6 +214,8 @@ static void MemoryUsage(const FunctionCallbackInfo<Value>& args) {
|
|||||||
fields[1] = v8_heap_stats.total_heap_size();
|
fields[1] = v8_heap_stats.total_heap_size();
|
||||||
fields[2] = v8_heap_stats.used_heap_size();
|
fields[2] = v8_heap_stats.used_heap_size();
|
||||||
fields[3] = v8_heap_stats.external_memory();
|
fields[3] = v8_heap_stats.external_memory();
|
||||||
|
fields[4] = array_buffer_allocator == nullptr ?
|
||||||
|
0 : array_buffer_allocator->total_mem_usage();
|
||||||
}
|
}
|
||||||
|
|
||||||
void RawDebug(const FunctionCallbackInfo<Value>& args) {
|
void RawDebug(const FunctionCallbackInfo<Value>& args) {
|
||||||
|
@ -30,3 +30,16 @@ if (!common.isIBMi)
|
|||||||
assert.ok(r.heapTotal > 0);
|
assert.ok(r.heapTotal > 0);
|
||||||
assert.ok(r.heapUsed > 0);
|
assert.ok(r.heapUsed > 0);
|
||||||
assert.ok(r.external > 0);
|
assert.ok(r.external > 0);
|
||||||
|
|
||||||
|
assert.strictEqual(typeof r.arrayBuffers, 'number');
|
||||||
|
if (r.arrayBuffers > 0) {
|
||||||
|
const size = 10 * 1024 * 1024;
|
||||||
|
// eslint-disable-next-line no-unused-vars
|
||||||
|
const ab = new ArrayBuffer(size);
|
||||||
|
|
||||||
|
const after = process.memoryUsage();
|
||||||
|
assert(after.external - r.external >= size,
|
||||||
|
`${after.external} - ${r.external} >= ${size}`);
|
||||||
|
assert.strictEqual(after.arrayBuffers - r.arrayBuffers, size,
|
||||||
|
`${after.arrayBuffers} - ${r.arrayBuffers} >= ${size}`);
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user