Browse Source

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>
v4.x
Ben Ripkens 9 years ago
committed by Myles Borins
parent
commit
30d60cf81c
No known key found for this signature in database GPG Key ID: 933B01F40B5CA946
  1. 49
      doc/api/v8.md
  2. 33
      lib/v8.js
  3. 12
      src/env-inl.h
  4. 4
      src/env.h
  5. 87
      src/node_v8.cc
  6. 19
      test/parallel/test-v8-stats.js

49
doc/api/v8.md

@ -24,6 +24,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)
<!-- YAML <!-- YAML
added: v1.0.0 added: v1.0.0

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 (var 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;
};

12
src/env-inl.h

@ -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_;
} }
@ -387,6 +388,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_;
} }

4
src/env.h

@ -460,6 +460,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);
@ -563,6 +566,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_;

87
src/node_v8.cc

@ -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

19
test/parallel/test-v8-stats.js

@ -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…
Cancel
Save