Skip to content

Commit 5ec9295

Browse files
addaleaxtargos
authored andcommitted
process: report ArrayBuffer memory in memoryUsage()
Report memory allocations performed by the `ArrayBuffer::Allocator`. PR-URL: #31550 Reviewed-By: Richard Lau <[email protected]> Reviewed-By: Colin Ihrig <[email protected]> Reviewed-By: Gus Caplan <[email protected]> Reviewed-By: Ben Noordhuis <[email protected]> Reviewed-By: James M Snell <[email protected]> Reviewed-By: Rich Trott <[email protected]> Reviewed-By: Gireesh Punathil <[email protected]>
1 parent 65ebfb2 commit 5ec9295

File tree

6 files changed

+80
-23
lines changed

6 files changed

+80
-23
lines changed

doc/api/process.md

+18-10
Original file line numberDiff line numberDiff line change
@@ -1488,6 +1488,9 @@ is no entry script.
14881488
<!-- YAML
14891489
added: v0.1.16
14901490
changes:
1491+
- version: REPLACEME
1492+
pr-url: https://github.com./nodejs/node/pull/31550
1493+
description: Added `arrayBuffers` to the returned object.
14911494
- version: v7.2.0
14921495
pr-url: https://github.com./nodejs/node/pull/9587
14931496
description: Added `external` to the returned object.
@@ -1498,6 +1501,7 @@ changes:
14981501
* `heapTotal` {integer}
14991502
* `heapUsed` {integer}
15001503
* `external` {integer}
1504+
* `arrayBuffers` {integer}
15011505

15021506
The `process.memoryUsage()` method returns an object describing the memory usage
15031507
of the Node.js process measured in bytes.
@@ -1516,19 +1520,22 @@ Will generate:
15161520
rss: 4935680,
15171521
heapTotal: 1826816,
15181522
heapUsed: 650472,
1519-
external: 49879
1523+
external: 49879,
1524+
arrayBuffers: 9386
15201525
}
15211526
```
15221527

1523-
`heapTotal` and `heapUsed` refer to V8's memory usage.
1524-
`external` refers to the memory usage of C++ objects bound to JavaScript
1525-
objects managed by V8. `rss`, Resident Set Size, is the amount of space
1526-
occupied in the main memory device (that is a subset of the total allocated
1527-
memory) for the process, which includes the _heap_, _code segment_ and _stack_.
1528-
1529-
The _heap_ is where objects, strings, and closures are stored. Variables are
1530-
stored in the _stack_ and the actual JavaScript code resides in the
1531-
_code segment_.
1528+
* `heapTotal` and `heapUsed` refer to V8's memory usage.
1529+
* `external` refers to the memory usage of C++ objects bound to JavaScript
1530+
objects managed by V8.
1531+
* `rss`, Resident Set Size, is the amount of space occupied in the main
1532+
memory device (that is a subset of the total allocated memory) for the
1533+
process, including all C++ and JavaScript objects and code.
1534+
* `arrayBuffers` refers to memory allocated for `ArrayBuffer`s and
1535+
`SharedArrayBuffer`s, including all Node.js [`Buffer`][]s.
1536+
This is also included in the `external` value. When Node.js is used as an
1537+
embedded library, this value may be `0` because allocations for `ArrayBuffer`s
1538+
may not be tracked in that case.
15321539

15331540
When using [`Worker`][] threads, `rss` will be a value that is valid for the
15341541
entire process, while the other fields will only refer to the current thread.
@@ -2497,6 +2504,7 @@ cases:
24972504
[`'exit'`]: #process_event_exit
24982505
[`'message'`]: child_process.html#child_process_event_message
24992506
[`'uncaughtException'`]: #process_event_uncaughtexception
2507+
[`Buffer`]: buffer.html
25002508
[`ChildProcess.disconnect()`]: child_process.html#child_process_subprocess_disconnect
25012509
[`ChildProcess.send()`]: child_process.html#child_process_subprocess_send_message_sendhandle_options_callback
25022510
[`ChildProcess`]: child_process.html#child_process_class_childprocess

lib/internal/process/per_thread.js

+3-2
Original file line numberDiff line numberDiff line change
@@ -146,14 +146,15 @@ function wrapProcessMethods(binding) {
146146
return hrBigintValues[0];
147147
}
148148

149-
const memValues = new Float64Array(4);
149+
const memValues = new Float64Array(5);
150150
function memoryUsage() {
151151
_memoryUsage(memValues);
152152
return {
153153
rss: memValues[0],
154154
heapTotal: memValues[1],
155155
heapUsed: memValues[2],
156-
external: memValues[3]
156+
external: memValues[3],
157+
arrayBuffers: memValues[4]
157158
};
158159
}
159160

src/api/environment.cc

+28-2
Original file line numberDiff line numberDiff line change
@@ -87,10 +87,34 @@ static void HostCleanupFinalizationGroupCallback(
8787
}
8888

8989
void* NodeArrayBufferAllocator::Allocate(size_t size) {
90+
void* ret;
9091
if (zero_fill_field_ || per_process::cli_options->zero_fill_all_buffers)
91-
return UncheckedCalloc(size);
92+
ret = UncheckedCalloc(size);
9293
else
93-
return UncheckedMalloc(size);
94+
ret = UncheckedMalloc(size);
95+
if (LIKELY(ret != nullptr))
96+
total_mem_usage_.fetch_add(size, std::memory_order_relaxed);
97+
return ret;
98+
}
99+
100+
void* NodeArrayBufferAllocator::AllocateUninitialized(size_t size) {
101+
void* ret = node::UncheckedMalloc(size);
102+
if (LIKELY(ret != nullptr))
103+
total_mem_usage_.fetch_add(size, std::memory_order_relaxed);
104+
return ret;
105+
}
106+
107+
void* NodeArrayBufferAllocator::Reallocate(
108+
void* data, size_t old_size, size_t size) {
109+
void* ret = UncheckedRealloc<char>(static_cast<char*>(data), size);
110+
if (LIKELY(ret != nullptr) || UNLIKELY(size == 0))
111+
total_mem_usage_.fetch_add(size - old_size, std::memory_order_relaxed);
112+
return ret;
113+
}
114+
115+
void NodeArrayBufferAllocator::Free(void* data, size_t size) {
116+
total_mem_usage_.fetch_sub(size, std::memory_order_relaxed);
117+
free(data);
94118
}
95119

96120
DebuggingArrayBufferAllocator::~DebuggingArrayBufferAllocator() {
@@ -140,11 +164,13 @@ void* DebuggingArrayBufferAllocator::Reallocate(void* data,
140164

141165
void DebuggingArrayBufferAllocator::RegisterPointer(void* data, size_t size) {
142166
Mutex::ScopedLock lock(mutex_);
167+
NodeArrayBufferAllocator::RegisterPointer(data, size);
143168
RegisterPointerInternal(data, size);
144169
}
145170

146171
void DebuggingArrayBufferAllocator::UnregisterPointer(void* data, size_t size) {
147172
Mutex::ScopedLock lock(mutex_);
173+
NodeArrayBufferAllocator::UnregisterPointer(data, size);
148174
UnregisterPointerInternal(data, size);
149175
}
150176

src/node_internals.h

+12-8
Original file line numberDiff line numberDiff line change
@@ -112,20 +112,24 @@ class NodeArrayBufferAllocator : public ArrayBufferAllocator {
112112
inline uint32_t* zero_fill_field() { return &zero_fill_field_; }
113113

114114
void* Allocate(size_t size) override; // Defined in src/node.cc
115-
void* AllocateUninitialized(size_t size) override
116-
{ return node::UncheckedMalloc(size); }
117-
void Free(void* data, size_t) override { free(data); }
118-
virtual void* Reallocate(void* data, size_t old_size, size_t size) {
119-
return static_cast<void*>(
120-
UncheckedRealloc<char>(static_cast<char*>(data), size));
115+
void* AllocateUninitialized(size_t size) override;
116+
void Free(void* data, size_t size) override;
117+
virtual void* Reallocate(void* data, size_t old_size, size_t size);
118+
virtual void RegisterPointer(void* data, size_t size) {
119+
total_mem_usage_.fetch_add(size, std::memory_order_relaxed);
120+
}
121+
virtual void UnregisterPointer(void* data, size_t size) {
122+
total_mem_usage_.fetch_sub(size, std::memory_order_relaxed);
121123
}
122-
virtual void RegisterPointer(void* data, size_t size) {}
123-
virtual void UnregisterPointer(void* data, size_t size) {}
124124

125125
NodeArrayBufferAllocator* GetImpl() final { return this; }
126+
inline uint64_t total_mem_usage() const {
127+
return total_mem_usage_.load(std::memory_order_relaxed);
128+
}
126129

127130
private:
128131
uint32_t zero_fill_field_ = 1; // Boolean but exposed as uint32 to JS land.
132+
std::atomic<size_t> total_mem_usage_ {0};
129133
};
130134

131135
class DebuggingArrayBufferAllocator final : public NodeArrayBufferAllocator {

src/node_process_methods.cc

+6-1
Original file line numberDiff line numberDiff line change
@@ -193,17 +193,22 @@ static void MemoryUsage(const FunctionCallbackInfo<Value>& args) {
193193
HeapStatistics v8_heap_stats;
194194
isolate->GetHeapStatistics(&v8_heap_stats);
195195

196+
NodeArrayBufferAllocator* array_buffer_allocator =
197+
env->isolate_data()->node_allocator();
198+
196199
// Get the double array pointer from the Float64Array argument.
197200
CHECK(args[0]->IsFloat64Array());
198201
Local<Float64Array> array = args[0].As<Float64Array>();
199-
CHECK_EQ(array->Length(), 4);
202+
CHECK_EQ(array->Length(), 5);
200203
Local<ArrayBuffer> ab = array->Buffer();
201204
double* fields = static_cast<double*>(ab->GetContents().Data());
202205

203206
fields[0] = rss;
204207
fields[1] = v8_heap_stats.total_heap_size();
205208
fields[2] = v8_heap_stats.used_heap_size();
206209
fields[3] = v8_heap_stats.external_memory();
210+
fields[4] = array_buffer_allocator == nullptr ?
211+
0 : array_buffer_allocator->total_mem_usage();
207212
}
208213

209214
void RawDebug(const FunctionCallbackInfo<Value>& args) {

test/parallel/test-memory-usage.js

+13
Original file line numberDiff line numberDiff line change
@@ -30,3 +30,16 @@ if (!common.isIBMi)
3030
assert.ok(r.heapTotal > 0);
3131
assert.ok(r.heapUsed > 0);
3232
assert.ok(r.external > 0);
33+
34+
assert.strictEqual(typeof r.arrayBuffers, 'number');
35+
if (r.arrayBuffers > 0) {
36+
const size = 10 * 1024 * 1024;
37+
// eslint-disable-next-line no-unused-vars
38+
const ab = new ArrayBuffer(size);
39+
40+
const after = process.memoryUsage();
41+
assert(after.external - r.external >= size,
42+
`${after.external} - ${r.external} >= ${size}`);
43+
assert.strictEqual(after.arrayBuffers - r.arrayBuffers, size,
44+
`${after.arrayBuffers} - ${r.arrayBuffers} >= ${size}`);
45+
}

0 commit comments

Comments
 (0)