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: https://github.com/nodejs/node/issues/2079 PR-URL: https://github.com/nodejs/node/pull/4463 Reviewed-By: Colin Ihrig <cjihrig@gmail.com> Reviewed-By: Trevor Norris <trev.norris@gmail.com> Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl> Reviewed-By: James M Snell <jasnell@gmail.com>
This commit is contained in:
parent
e65f1f7954
commit
5f57005ec9
@ -21,6 +21,55 @@ Returns an object with the following properties
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## getHeapSpaceStatistics()
|
||||||
|
|
||||||
|
Returns statistics about the V8 heap spaces, i.e. the segments which make up
|
||||||
|
the V8 heap. Order of heap spaces nor availability of a heap space can be
|
||||||
|
guaranteed as the statistics are provided via the V8 `GetHeapSpaceStatistics`
|
||||||
|
function.
|
||||||
|
|
||||||
|
Example result:
|
||||||
|
|
||||||
|
```
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"space_name": "new_space",
|
||||||
|
"space_size": 2063872,
|
||||||
|
"space_used_size": 951112,
|
||||||
|
"space_available_size": 80824,
|
||||||
|
"physical_space_size": 2063872
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"space_name": "old_space",
|
||||||
|
"space_size": 3090560,
|
||||||
|
"space_used_size": 2493792,
|
||||||
|
"space_available_size": 0,
|
||||||
|
"physical_space_size": 3090560
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"space_name": "code_space",
|
||||||
|
"space_size": 1260160,
|
||||||
|
"space_used_size": 644256,
|
||||||
|
"space_available_size": 960,
|
||||||
|
"physical_space_size": 1260160
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"space_name": "map_space",
|
||||||
|
"space_size": 1094160,
|
||||||
|
"space_used_size": 201608,
|
||||||
|
"space_available_size": 0,
|
||||||
|
"physical_space_size": 1094160
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"space_name": "large_object_space",
|
||||||
|
"space_size": 0,
|
||||||
|
"space_used_size": 0,
|
||||||
|
"space_available_size": 1490980608,
|
||||||
|
"physical_space_size": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
## setFlagsFromString(string)
|
## setFlagsFromString(string)
|
||||||
|
|
||||||
Set additional V8 command line flags. Use with care; changing settings
|
Set additional V8 command line flags. Use with care; changing settings
|
||||||
|
33
lib/v8.js
33
lib/v8.js
@ -16,9 +16,9 @@
|
|||||||
|
|
||||||
const v8binding = process.binding('v8');
|
const v8binding = process.binding('v8');
|
||||||
|
|
||||||
|
// Properties for heap statistics buffer extraction.
|
||||||
const heapStatisticsBuffer =
|
const heapStatisticsBuffer =
|
||||||
new Uint32Array(v8binding.heapStatisticsArrayBuffer);
|
new Uint32Array(v8binding.heapStatisticsArrayBuffer);
|
||||||
|
|
||||||
const kTotalHeapSizeIndex = v8binding.kTotalHeapSizeIndex;
|
const kTotalHeapSizeIndex = v8binding.kTotalHeapSizeIndex;
|
||||||
const kTotalHeapSizeExecutableIndex = v8binding.kTotalHeapSizeExecutableIndex;
|
const kTotalHeapSizeExecutableIndex = v8binding.kTotalHeapSizeExecutableIndex;
|
||||||
const kTotalPhysicalSizeIndex = v8binding.kTotalPhysicalSizeIndex;
|
const kTotalPhysicalSizeIndex = v8binding.kTotalPhysicalSizeIndex;
|
||||||
@ -26,6 +26,18 @@ const kTotalAvailableSize = v8binding.kTotalAvailableSize;
|
|||||||
const kUsedHeapSizeIndex = v8binding.kUsedHeapSizeIndex;
|
const kUsedHeapSizeIndex = v8binding.kUsedHeapSizeIndex;
|
||||||
const kHeapSizeLimitIndex = v8binding.kHeapSizeLimitIndex;
|
const kHeapSizeLimitIndex = v8binding.kHeapSizeLimitIndex;
|
||||||
|
|
||||||
|
// Properties for heap space statistics buffer extraction.
|
||||||
|
const heapSpaceStatisticsBuffer =
|
||||||
|
new Uint32Array(v8binding.heapSpaceStatisticsArrayBuffer);
|
||||||
|
const kHeapSpaces = v8binding.kHeapSpaces;
|
||||||
|
const kNumberOfHeapSpaces = kHeapSpaces.length;
|
||||||
|
const kHeapSpaceStatisticsPropertiesCount =
|
||||||
|
v8binding.kHeapSpaceStatisticsPropertiesCount;
|
||||||
|
const kSpaceSizeIndex = v8binding.kSpaceSizeIndex;
|
||||||
|
const kSpaceUsedSizeIndex = v8binding.kSpaceUsedSizeIndex;
|
||||||
|
const kSpaceAvailableSizeIndex = v8binding.kSpaceAvailableSizeIndex;
|
||||||
|
const kPhysicalSpaceSizeIndex = v8binding.kPhysicalSpaceSizeIndex;
|
||||||
|
|
||||||
exports.getHeapStatistics = function() {
|
exports.getHeapStatistics = function() {
|
||||||
const buffer = heapStatisticsBuffer;
|
const buffer = heapStatisticsBuffer;
|
||||||
|
|
||||||
@ -42,3 +54,22 @@ exports.getHeapStatistics = function() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
exports.setFlagsFromString = v8binding.setFlagsFromString;
|
exports.setFlagsFromString = v8binding.setFlagsFromString;
|
||||||
|
|
||||||
|
exports.getHeapSpaceStatistics = function() {
|
||||||
|
const heapSpaceStatistics = new Array(kNumberOfHeapSpaces);
|
||||||
|
const buffer = heapSpaceStatisticsBuffer;
|
||||||
|
v8binding.updateHeapSpaceStatisticsArrayBuffer();
|
||||||
|
|
||||||
|
for (let i = 0; i < kNumberOfHeapSpaces; i++) {
|
||||||
|
const propertyOffset = i * kHeapSpaceStatisticsPropertiesCount;
|
||||||
|
heapSpaceStatistics[i] = {
|
||||||
|
space_name: kHeapSpaces[i],
|
||||||
|
space_size: buffer[propertyOffset + kSpaceSizeIndex],
|
||||||
|
space_used_size: buffer[propertyOffset + kSpaceUsedSizeIndex],
|
||||||
|
space_available_size: buffer[propertyOffset + kSpaceAvailableSizeIndex],
|
||||||
|
physical_space_size: buffer[propertyOffset + kPhysicalSpaceSizeIndex]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return heapSpaceStatistics;
|
||||||
|
};
|
||||||
|
@ -242,6 +242,7 @@ inline Environment::~Environment() {
|
|||||||
isolate_data()->Put();
|
isolate_data()->Put();
|
||||||
|
|
||||||
delete[] heap_statistics_buffer_;
|
delete[] heap_statistics_buffer_;
|
||||||
|
delete[] heap_space_statistics_buffer_;
|
||||||
delete[] http_parser_buffer_;
|
delete[] http_parser_buffer_;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -374,6 +375,17 @@ inline void Environment::set_heap_statistics_buffer(uint32_t* pointer) {
|
|||||||
heap_statistics_buffer_ = pointer;
|
heap_statistics_buffer_ = pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline uint32_t* Environment::heap_space_statistics_buffer() const {
|
||||||
|
CHECK_NE(heap_space_statistics_buffer_, nullptr);
|
||||||
|
return heap_space_statistics_buffer_;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void Environment::set_heap_space_statistics_buffer(uint32_t* pointer) {
|
||||||
|
CHECK_EQ(heap_space_statistics_buffer_, nullptr); // Should be set only once.
|
||||||
|
heap_space_statistics_buffer_ = pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
inline char* Environment::http_parser_buffer() const {
|
inline char* Environment::http_parser_buffer() const {
|
||||||
return http_parser_buffer_;
|
return http_parser_buffer_;
|
||||||
}
|
}
|
||||||
|
@ -462,6 +462,9 @@ class Environment {
|
|||||||
inline uint32_t* heap_statistics_buffer() const;
|
inline uint32_t* heap_statistics_buffer() const;
|
||||||
inline void set_heap_statistics_buffer(uint32_t* pointer);
|
inline void set_heap_statistics_buffer(uint32_t* pointer);
|
||||||
|
|
||||||
|
inline uint32_t* heap_space_statistics_buffer() const;
|
||||||
|
inline void set_heap_space_statistics_buffer(uint32_t* pointer);
|
||||||
|
|
||||||
inline char* http_parser_buffer() const;
|
inline char* http_parser_buffer() const;
|
||||||
inline void set_http_parser_buffer(char* buffer);
|
inline void set_http_parser_buffer(char* buffer);
|
||||||
|
|
||||||
@ -562,6 +565,7 @@ class Environment {
|
|||||||
int handle_cleanup_waiting_;
|
int handle_cleanup_waiting_;
|
||||||
|
|
||||||
uint32_t* heap_statistics_buffer_ = nullptr;
|
uint32_t* heap_statistics_buffer_ = nullptr;
|
||||||
|
uint32_t* heap_space_statistics_buffer_ = nullptr;
|
||||||
|
|
||||||
char* http_parser_buffer_;
|
char* http_parser_buffer_;
|
||||||
|
|
||||||
|
@ -7,13 +7,16 @@
|
|||||||
|
|
||||||
namespace node {
|
namespace node {
|
||||||
|
|
||||||
|
using v8::Array;
|
||||||
using v8::ArrayBuffer;
|
using v8::ArrayBuffer;
|
||||||
using v8::Context;
|
using v8::Context;
|
||||||
using v8::Function;
|
using v8::Function;
|
||||||
using v8::FunctionCallbackInfo;
|
using v8::FunctionCallbackInfo;
|
||||||
|
using v8::HeapSpaceStatistics;
|
||||||
using v8::HeapStatistics;
|
using v8::HeapStatistics;
|
||||||
using v8::Isolate;
|
using v8::Isolate;
|
||||||
using v8::Local;
|
using v8::Local;
|
||||||
|
using v8::NewStringType;
|
||||||
using v8::Object;
|
using v8::Object;
|
||||||
using v8::String;
|
using v8::String;
|
||||||
using v8::Uint32;
|
using v8::Uint32;
|
||||||
@ -34,6 +37,21 @@ static const size_t kHeapStatisticsPropertiesCount =
|
|||||||
HEAP_STATISTICS_PROPERTIES(V);
|
HEAP_STATISTICS_PROPERTIES(V);
|
||||||
#undef V
|
#undef V
|
||||||
|
|
||||||
|
#define HEAP_SPACE_STATISTICS_PROPERTIES(V) \
|
||||||
|
V(0, space_size, kSpaceSizeIndex) \
|
||||||
|
V(1, space_used_size, kSpaceUsedSizeIndex) \
|
||||||
|
V(2, space_available_size, kSpaceAvailableSizeIndex) \
|
||||||
|
V(3, physical_space_size, kPhysicalSpaceSizeIndex)
|
||||||
|
|
||||||
|
#define V(a, b, c) +1
|
||||||
|
static const size_t kHeapSpaceStatisticsPropertiesCount =
|
||||||
|
HEAP_SPACE_STATISTICS_PROPERTIES(V);
|
||||||
|
#undef V
|
||||||
|
|
||||||
|
// Will be populated in InitializeV8Bindings.
|
||||||
|
static size_t number_of_heap_spaces = 0;
|
||||||
|
|
||||||
|
|
||||||
void UpdateHeapStatisticsArrayBuffer(const FunctionCallbackInfo<Value>& args) {
|
void UpdateHeapStatisticsArrayBuffer(const FunctionCallbackInfo<Value>& args) {
|
||||||
Environment* env = Environment::GetCurrent(args);
|
Environment* env = Environment::GetCurrent(args);
|
||||||
HeapStatistics s;
|
HeapStatistics s;
|
||||||
@ -45,6 +63,23 @@ void UpdateHeapStatisticsArrayBuffer(const FunctionCallbackInfo<Value>& args) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void UpdateHeapSpaceStatisticsBuffer(const FunctionCallbackInfo<Value>& args) {
|
||||||
|
Environment* env = Environment::GetCurrent(args);
|
||||||
|
HeapSpaceStatistics s;
|
||||||
|
Isolate* const isolate = env->isolate();
|
||||||
|
uint32_t* buffer = env->heap_space_statistics_buffer();
|
||||||
|
|
||||||
|
for (size_t i = 0; i < number_of_heap_spaces; i++) {
|
||||||
|
isolate->GetHeapSpaceStatistics(&s, i);
|
||||||
|
size_t const property_offset = i * kHeapSpaceStatisticsPropertiesCount;
|
||||||
|
#define V(index, name, _) buffer[property_offset + index] = \
|
||||||
|
static_cast<uint32_t>(s.name());
|
||||||
|
HEAP_SPACE_STATISTICS_PROPERTIES(V)
|
||||||
|
#undef V
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void SetFlagsFromString(const FunctionCallbackInfo<Value>& args) {
|
void SetFlagsFromString(const FunctionCallbackInfo<Value>& args) {
|
||||||
Environment* env = Environment::GetCurrent(args);
|
Environment* env = Environment::GetCurrent(args);
|
||||||
|
|
||||||
@ -62,10 +97,10 @@ void InitializeV8Bindings(Local<Object> target,
|
|||||||
Local<Value> unused,
|
Local<Value> unused,
|
||||||
Local<Context> context) {
|
Local<Context> context) {
|
||||||
Environment* env = Environment::GetCurrent(context);
|
Environment* env = Environment::GetCurrent(context);
|
||||||
|
|
||||||
env->SetMethod(target,
|
env->SetMethod(target,
|
||||||
"updateHeapStatisticsArrayBuffer",
|
"updateHeapStatisticsArrayBuffer",
|
||||||
UpdateHeapStatisticsArrayBuffer);
|
UpdateHeapStatisticsArrayBuffer);
|
||||||
env->SetMethod(target, "setFlagsFromString", SetFlagsFromString);
|
|
||||||
|
|
||||||
env->set_heap_statistics_buffer(new uint32_t[kHeapStatisticsPropertiesCount]);
|
env->set_heap_statistics_buffer(new uint32_t[kHeapStatisticsPropertiesCount]);
|
||||||
|
|
||||||
@ -84,6 +119,56 @@ void InitializeV8Bindings(Local<Object> target,
|
|||||||
|
|
||||||
HEAP_STATISTICS_PROPERTIES(V)
|
HEAP_STATISTICS_PROPERTIES(V)
|
||||||
#undef V
|
#undef V
|
||||||
|
|
||||||
|
target->Set(FIXED_ONE_BYTE_STRING(env->isolate(),
|
||||||
|
"kHeapSpaceStatisticsPropertiesCount"),
|
||||||
|
Uint32::NewFromUnsigned(env->isolate(),
|
||||||
|
kHeapSpaceStatisticsPropertiesCount));
|
||||||
|
|
||||||
|
number_of_heap_spaces = env->isolate()->NumberOfHeapSpaces();
|
||||||
|
|
||||||
|
// Heap space names are extracted once and exposed to JavaScript to
|
||||||
|
// avoid excessive creation of heap space name Strings.
|
||||||
|
HeapSpaceStatistics s;
|
||||||
|
const Local<Array> heap_spaces = Array::New(env->isolate(),
|
||||||
|
number_of_heap_spaces);
|
||||||
|
for (size_t i = 0; i < number_of_heap_spaces; i++) {
|
||||||
|
env->isolate()->GetHeapSpaceStatistics(&s, i);
|
||||||
|
Local<String> heap_space_name = String::NewFromUtf8(env->isolate(),
|
||||||
|
s.space_name(),
|
||||||
|
NewStringType::kNormal)
|
||||||
|
.ToLocalChecked();
|
||||||
|
heap_spaces->Set(i, heap_space_name);
|
||||||
|
}
|
||||||
|
target->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "kHeapSpaces"),
|
||||||
|
heap_spaces);
|
||||||
|
|
||||||
|
env->SetMethod(target,
|
||||||
|
"updateHeapSpaceStatisticsArrayBuffer",
|
||||||
|
UpdateHeapSpaceStatisticsBuffer);
|
||||||
|
|
||||||
|
env->set_heap_space_statistics_buffer(
|
||||||
|
new uint32_t[kHeapSpaceStatisticsPropertiesCount * number_of_heap_spaces]);
|
||||||
|
|
||||||
|
const size_t heap_space_statistics_buffer_byte_length =
|
||||||
|
sizeof(*env->heap_space_statistics_buffer()) *
|
||||||
|
kHeapSpaceStatisticsPropertiesCount *
|
||||||
|
number_of_heap_spaces;
|
||||||
|
|
||||||
|
target->Set(FIXED_ONE_BYTE_STRING(env->isolate(),
|
||||||
|
"heapSpaceStatisticsArrayBuffer"),
|
||||||
|
ArrayBuffer::New(env->isolate(),
|
||||||
|
env->heap_space_statistics_buffer(),
|
||||||
|
heap_space_statistics_buffer_byte_length));
|
||||||
|
|
||||||
|
#define V(i, _, name) \
|
||||||
|
target->Set(FIXED_ONE_BYTE_STRING(env->isolate(), #name), \
|
||||||
|
Uint32::NewFromUnsigned(env->isolate(), i));
|
||||||
|
|
||||||
|
HEAP_SPACE_STATISTICS_PROPERTIES(V)
|
||||||
|
#undef V
|
||||||
|
|
||||||
|
env->SetMethod(target, "setFlagsFromString", SetFlagsFromString);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace node
|
} // namespace node
|
||||||
|
@ -15,3 +15,22 @@ assert.deepEqual(Object.keys(s).sort(), keys);
|
|||||||
keys.forEach(function(key) {
|
keys.forEach(function(key) {
|
||||||
assert.equal(typeof s[key], 'number');
|
assert.equal(typeof s[key], 'number');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
const expectedHeapSpaces = [
|
||||||
|
'new_space',
|
||||||
|
'old_space',
|
||||||
|
'code_space',
|
||||||
|
'map_space',
|
||||||
|
'large_object_space'
|
||||||
|
];
|
||||||
|
const heapSpaceStatistics = v8.getHeapSpaceStatistics();
|
||||||
|
const actualHeapSpaceNames = heapSpaceStatistics.map(s => s.space_name);
|
||||||
|
assert.deepEqual(actualHeapSpaceNames.sort(), expectedHeapSpaces.sort());
|
||||||
|
heapSpaceStatistics.forEach(heapSpace => {
|
||||||
|
assert.strictEqual(typeof heapSpace.space_name, 'string');
|
||||||
|
assert.strictEqual(typeof heapSpace.space_size, 'number');
|
||||||
|
assert.strictEqual(typeof heapSpace.space_used_size, 'number');
|
||||||
|
assert.strictEqual(typeof heapSpace.space_available_size, 'number');
|
||||||
|
assert.strictEqual(typeof heapSpace.physical_space_size, 'number');
|
||||||
|
});
|
||||||
|
Loading…
x
Reference in New Issue
Block a user