Skip to content

Commit 7d29fc2

Browse files
bripkensMichael Scovetta
authored and
Michael Scovetta
committed
v8,src: expose statistics about heap spaces
Provide means to inspect information about the separate heap spaces via a callable API. This is helpful to analyze memory issues. Fixes: nodejs#2079 PR-URL: nodejs#4463 Reviewed-By: Colin Ihrig <[email protected]> Reviewed-By: Trevor Norris <[email protected]> Reviewed-By: Ben Noordhuis <[email protected]> Reviewed-By: James M Snell <[email protected]>
1 parent 5108f39 commit 7d29fc2

File tree

6 files changed

+202
-2
lines changed

6 files changed

+202
-2
lines changed

doc/api/v8.markdown

+49
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,55 @@ Returns an object with the following properties
2121
}
2222
```
2323

24+
## getHeapSpaceStatistics()
25+
26+
Returns statistics about the V8 heap spaces, i.e. the segments which make up
27+
the V8 heap. Order of heap spaces nor availability of a heap space can be
28+
guaranteed as the statistics are provided via the V8 `GetHeapSpaceStatistics`
29+
function.
30+
31+
Example result:
32+
33+
```
34+
[
35+
{
36+
"space_name": "new_space",
37+
"space_size": 2063872,
38+
"space_used_size": 951112,
39+
"space_available_size": 80824,
40+
"physical_space_size": 2063872
41+
},
42+
{
43+
"space_name": "old_space",
44+
"space_size": 3090560,
45+
"space_used_size": 2493792,
46+
"space_available_size": 0,
47+
"physical_space_size": 3090560
48+
},
49+
{
50+
"space_name": "code_space",
51+
"space_size": 1260160,
52+
"space_used_size": 644256,
53+
"space_available_size": 960,
54+
"physical_space_size": 1260160
55+
},
56+
{
57+
"space_name": "map_space",
58+
"space_size": 1094160,
59+
"space_used_size": 201608,
60+
"space_available_size": 0,
61+
"physical_space_size": 1094160
62+
},
63+
{
64+
"space_name": "large_object_space",
65+
"space_size": 0,
66+
"space_used_size": 0,
67+
"space_available_size": 1490980608,
68+
"physical_space_size": 0
69+
}
70+
]
71+
```
72+
2473
## setFlagsFromString(string)
2574

2675
Set additional V8 command line flags. Use with care; changing settings

lib/v8.js

+32-1
Original file line numberDiff line numberDiff line change
@@ -16,16 +16,28 @@
1616

1717
const v8binding = process.binding('v8');
1818

19+
// Properties for heap statistics buffer extraction.
1920
const heapStatisticsBuffer =
2021
new Uint32Array(v8binding.heapStatisticsArrayBuffer);
21-
2222
const kTotalHeapSizeIndex = v8binding.kTotalHeapSizeIndex;
2323
const kTotalHeapSizeExecutableIndex = v8binding.kTotalHeapSizeExecutableIndex;
2424
const kTotalPhysicalSizeIndex = v8binding.kTotalPhysicalSizeIndex;
2525
const kTotalAvailableSize = v8binding.kTotalAvailableSize;
2626
const kUsedHeapSizeIndex = v8binding.kUsedHeapSizeIndex;
2727
const kHeapSizeLimitIndex = v8binding.kHeapSizeLimitIndex;
2828

29+
// Properties for heap space statistics buffer extraction.
30+
const heapSpaceStatisticsBuffer =
31+
new Uint32Array(v8binding.heapSpaceStatisticsArrayBuffer);
32+
const kHeapSpaces = v8binding.kHeapSpaces;
33+
const kNumberOfHeapSpaces = kHeapSpaces.length;
34+
const kHeapSpaceStatisticsPropertiesCount =
35+
v8binding.kHeapSpaceStatisticsPropertiesCount;
36+
const kSpaceSizeIndex = v8binding.kSpaceSizeIndex;
37+
const kSpaceUsedSizeIndex = v8binding.kSpaceUsedSizeIndex;
38+
const kSpaceAvailableSizeIndex = v8binding.kSpaceAvailableSizeIndex;
39+
const kPhysicalSpaceSizeIndex = v8binding.kPhysicalSpaceSizeIndex;
40+
2941
exports.getHeapStatistics = function() {
3042
const buffer = heapStatisticsBuffer;
3143

@@ -42,3 +54,22 @@ exports.getHeapStatistics = function() {
4254
};
4355

4456
exports.setFlagsFromString = v8binding.setFlagsFromString;
57+
58+
exports.getHeapSpaceStatistics = function() {
59+
const heapSpaceStatistics = new Array(kNumberOfHeapSpaces);
60+
const buffer = heapSpaceStatisticsBuffer;
61+
v8binding.updateHeapSpaceStatisticsArrayBuffer();
62+
63+
for (let i = 0; i < kNumberOfHeapSpaces; i++) {
64+
const propertyOffset = i * kHeapSpaceStatisticsPropertiesCount;
65+
heapSpaceStatistics[i] = {
66+
space_name: kHeapSpaces[i],
67+
space_size: buffer[propertyOffset + kSpaceSizeIndex],
68+
space_used_size: buffer[propertyOffset + kSpaceUsedSizeIndex],
69+
space_available_size: buffer[propertyOffset + kSpaceAvailableSizeIndex],
70+
physical_space_size: buffer[propertyOffset + kPhysicalSpaceSizeIndex]
71+
};
72+
}
73+
74+
return heapSpaceStatistics;
75+
};

src/env-inl.h

+12
Original file line numberDiff line numberDiff line change
@@ -242,6 +242,7 @@ inline Environment::~Environment() {
242242
isolate_data()->Put();
243243

244244
delete[] heap_statistics_buffer_;
245+
delete[] heap_space_statistics_buffer_;
245246
delete[] http_parser_buffer_;
246247
}
247248

@@ -374,6 +375,17 @@ inline void Environment::set_heap_statistics_buffer(uint32_t* pointer) {
374375
heap_statistics_buffer_ = pointer;
375376
}
376377

378+
inline uint32_t* Environment::heap_space_statistics_buffer() const {
379+
CHECK_NE(heap_space_statistics_buffer_, nullptr);
380+
return heap_space_statistics_buffer_;
381+
}
382+
383+
inline void Environment::set_heap_space_statistics_buffer(uint32_t* pointer) {
384+
CHECK_EQ(heap_space_statistics_buffer_, nullptr); // Should be set only once.
385+
heap_space_statistics_buffer_ = pointer;
386+
}
387+
388+
377389
inline char* Environment::http_parser_buffer() const {
378390
return http_parser_buffer_;
379391
}

src/env.h

+4
Original file line numberDiff line numberDiff line change
@@ -462,6 +462,9 @@ class Environment {
462462
inline uint32_t* heap_statistics_buffer() const;
463463
inline void set_heap_statistics_buffer(uint32_t* pointer);
464464

465+
inline uint32_t* heap_space_statistics_buffer() const;
466+
inline void set_heap_space_statistics_buffer(uint32_t* pointer);
467+
465468
inline char* http_parser_buffer() const;
466469
inline void set_http_parser_buffer(char* buffer);
467470

@@ -562,6 +565,7 @@ class Environment {
562565
int handle_cleanup_waiting_;
563566

564567
uint32_t* heap_statistics_buffer_ = nullptr;
568+
uint32_t* heap_space_statistics_buffer_ = nullptr;
565569

566570
char* http_parser_buffer_;
567571

src/node_v8.cc

+86-1
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,16 @@
77

88
namespace node {
99

10+
using v8::Array;
1011
using v8::ArrayBuffer;
1112
using v8::Context;
1213
using v8::Function;
1314
using v8::FunctionCallbackInfo;
15+
using v8::HeapSpaceStatistics;
1416
using v8::HeapStatistics;
1517
using v8::Isolate;
1618
using v8::Local;
19+
using v8::NewStringType;
1720
using v8::Object;
1821
using v8::String;
1922
using v8::Uint32;
@@ -34,6 +37,21 @@ static const size_t kHeapStatisticsPropertiesCount =
3437
HEAP_STATISTICS_PROPERTIES(V);
3538
#undef V
3639

40+
#define HEAP_SPACE_STATISTICS_PROPERTIES(V) \
41+
V(0, space_size, kSpaceSizeIndex) \
42+
V(1, space_used_size, kSpaceUsedSizeIndex) \
43+
V(2, space_available_size, kSpaceAvailableSizeIndex) \
44+
V(3, physical_space_size, kPhysicalSpaceSizeIndex)
45+
46+
#define V(a, b, c) +1
47+
static const size_t kHeapSpaceStatisticsPropertiesCount =
48+
HEAP_SPACE_STATISTICS_PROPERTIES(V);
49+
#undef V
50+
51+
// Will be populated in InitializeV8Bindings.
52+
static size_t number_of_heap_spaces = 0;
53+
54+
3755
void UpdateHeapStatisticsArrayBuffer(const FunctionCallbackInfo<Value>& args) {
3856
Environment* env = Environment::GetCurrent(args);
3957
HeapStatistics s;
@@ -45,6 +63,23 @@ void UpdateHeapStatisticsArrayBuffer(const FunctionCallbackInfo<Value>& args) {
4563
}
4664

4765

66+
void UpdateHeapSpaceStatisticsBuffer(const FunctionCallbackInfo<Value>& args) {
67+
Environment* env = Environment::GetCurrent(args);
68+
HeapSpaceStatistics s;
69+
Isolate* const isolate = env->isolate();
70+
uint32_t* buffer = env->heap_space_statistics_buffer();
71+
72+
for (size_t i = 0; i < number_of_heap_spaces; i++) {
73+
isolate->GetHeapSpaceStatistics(&s, i);
74+
size_t const property_offset = i * kHeapSpaceStatisticsPropertiesCount;
75+
#define V(index, name, _) buffer[property_offset + index] = \
76+
static_cast<uint32_t>(s.name());
77+
HEAP_SPACE_STATISTICS_PROPERTIES(V)
78+
#undef V
79+
}
80+
}
81+
82+
4883
void SetFlagsFromString(const FunctionCallbackInfo<Value>& args) {
4984
Environment* env = Environment::GetCurrent(args);
5085

@@ -62,10 +97,10 @@ void InitializeV8Bindings(Local<Object> target,
6297
Local<Value> unused,
6398
Local<Context> context) {
6499
Environment* env = Environment::GetCurrent(context);
100+
65101
env->SetMethod(target,
66102
"updateHeapStatisticsArrayBuffer",
67103
UpdateHeapStatisticsArrayBuffer);
68-
env->SetMethod(target, "setFlagsFromString", SetFlagsFromString);
69104

70105
env->set_heap_statistics_buffer(new uint32_t[kHeapStatisticsPropertiesCount]);
71106

@@ -84,6 +119,56 @@ void InitializeV8Bindings(Local<Object> target,
84119

85120
HEAP_STATISTICS_PROPERTIES(V)
86121
#undef V
122+
123+
target->Set(FIXED_ONE_BYTE_STRING(env->isolate(),
124+
"kHeapSpaceStatisticsPropertiesCount"),
125+
Uint32::NewFromUnsigned(env->isolate(),
126+
kHeapSpaceStatisticsPropertiesCount));
127+
128+
number_of_heap_spaces = env->isolate()->NumberOfHeapSpaces();
129+
130+
// Heap space names are extracted once and exposed to JavaScript to
131+
// avoid excessive creation of heap space name Strings.
132+
HeapSpaceStatistics s;
133+
const Local<Array> heap_spaces = Array::New(env->isolate(),
134+
number_of_heap_spaces);
135+
for (size_t i = 0; i < number_of_heap_spaces; i++) {
136+
env->isolate()->GetHeapSpaceStatistics(&s, i);
137+
Local<String> heap_space_name = String::NewFromUtf8(env->isolate(),
138+
s.space_name(),
139+
NewStringType::kNormal)
140+
.ToLocalChecked();
141+
heap_spaces->Set(i, heap_space_name);
142+
}
143+
target->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "kHeapSpaces"),
144+
heap_spaces);
145+
146+
env->SetMethod(target,
147+
"updateHeapSpaceStatisticsArrayBuffer",
148+
UpdateHeapSpaceStatisticsBuffer);
149+
150+
env->set_heap_space_statistics_buffer(
151+
new uint32_t[kHeapSpaceStatisticsPropertiesCount * number_of_heap_spaces]);
152+
153+
const size_t heap_space_statistics_buffer_byte_length =
154+
sizeof(*env->heap_space_statistics_buffer()) *
155+
kHeapSpaceStatisticsPropertiesCount *
156+
number_of_heap_spaces;
157+
158+
target->Set(FIXED_ONE_BYTE_STRING(env->isolate(),
159+
"heapSpaceStatisticsArrayBuffer"),
160+
ArrayBuffer::New(env->isolate(),
161+
env->heap_space_statistics_buffer(),
162+
heap_space_statistics_buffer_byte_length));
163+
164+
#define V(i, _, name) \
165+
target->Set(FIXED_ONE_BYTE_STRING(env->isolate(), #name), \
166+
Uint32::NewFromUnsigned(env->isolate(), i));
167+
168+
HEAP_SPACE_STATISTICS_PROPERTIES(V)
169+
#undef V
170+
171+
env->SetMethod(target, "setFlagsFromString", SetFlagsFromString);
87172
}
88173

89174
} // namespace node

test/parallel/test-v8-stats.js

+19
Original file line numberDiff line numberDiff line change
@@ -15,3 +15,22 @@ assert.deepEqual(Object.keys(s).sort(), keys);
1515
keys.forEach(function(key) {
1616
assert.equal(typeof s[key], 'number');
1717
});
18+
19+
20+
const expectedHeapSpaces = [
21+
'new_space',
22+
'old_space',
23+
'code_space',
24+
'map_space',
25+
'large_object_space'
26+
];
27+
const heapSpaceStatistics = v8.getHeapSpaceStatistics();
28+
const actualHeapSpaceNames = heapSpaceStatistics.map(s => s.space_name);
29+
assert.deepEqual(actualHeapSpaceNames.sort(), expectedHeapSpaces.sort());
30+
heapSpaceStatistics.forEach(heapSpace => {
31+
assert.strictEqual(typeof heapSpace.space_name, 'string');
32+
assert.strictEqual(typeof heapSpace.space_size, 'number');
33+
assert.strictEqual(typeof heapSpace.space_used_size, 'number');
34+
assert.strictEqual(typeof heapSpace.space_available_size, 'number');
35+
assert.strictEqual(typeof heapSpace.physical_space_size, 'number');
36+
});

0 commit comments

Comments
 (0)